牛客题目解析

一.最长回文子串

1.题目:给定一个仅包含小写字母的字符串,求它的最长回文子串的长度。

最长回文子串__牛客网

2.算法原理:

<1>动态规划算法:O(n^2),O(n^2) 具有通性,凡涉及回文子串的问题都可利用此法解决

知识储备:对与只有一个字符的字符串一定是回文串;对于有两个字符的字符串当两个字符相等时,才是回文串;当字符串的长度大于2时,若该字符串是回文串则去掉首尾两个字符后仍是回文串。

创建一个n*n的dp表,统计[i,j]区间段内的子串是否是回文串。当i<j时,dp[i][j]=0;当i==j时,dp[i][j]==1;当j-i==1时,判断str[i]和str[j]是否相等;当j-i>1时,若str[i]==str[j],则判断dp[i+1][j-1]是否为1,若str[i]!=str[j],则dp[i][j]=0

注意:要是按照常规思维,利用字符串的长度两次循环的话,会出现判断dp[3][6]需要去看dp[4][5]的情形,而dp[4][5]还没有进行计算的情况,所以我们可以利用首尾字符间的距离作为第一层循环的自变量,从而避免访问还未判断位置的信息的情况


int getLongestPalindrome(string A) {
        int n=A.size();
        vector<vector<int>> r(n,vector<int>(n));
        int back;
        //创建矩阵在r中保存当前r[i][j]是否是回文子串,判定的依据为r[i+1][j-1]==1且A[i]==A[j]
        for(int d=0;d<n;d++)//d:两者间的间距
        {
            for(int i=0;i<n-d;i++)
            {
                int j=i+d;
                if(d==0)
                    r[i][j]=1;
                else if(d==1)
                    r[i][j]=(A[i]==A[j]);
                else
                    r[i][j]=(A[i]==A[j]&&r[i+1][j-1]==1);
                if(r[i][j]&&d+1>ret.size())
                {
                    back=j-i+1;
                }
            }
        }
        return back;

<2>马拉车算法:O(n),O(n)  不具有通性,只能用于解决这一种问题

此种算法思想小编暂时还没有掌握,后续若是掌握了会在评论区介绍的,有兴趣的宝子可以自己尝试理解,要是学会了记得教小编一下

<3>中心扩展算法:O(n^2),O(1)

遍历字符串,以拿到的字符str[i]为回文子串的中心字符,然后利用两个指针left和right以str[i]为中心,分别向左右扩展,直至不符合str[left]!=str[right],更新回文子串的最长长度。

注意:因为回文子串的长度可以是奇数也可以是偶数,所以对于初始时left的赋值应该考虑left=i,和left=i-1两种情况

#include <iostream>
#include<string>
using namespace std;

int main() {
    string str;
    cin>>str;
    
    //中心扩展算法
    int left=0,right=0,ret=0;
    for(int i=0;i<str.size();i++)
    {
        //长度为奇数的回文串
        left=i-1;
        right=i+1;
        while(left>=0 && right<str.size() && str[left]==str[right])
        {
            left--;
            right++;
        }
        ret=max(ret,right-left-1);
        //长度为偶数的回文串
        left=i;
        right=i+1;
        while(left>=0 && right<str.size() && str[left]==str[right])
        {
            left--;
            right++;
        }
        ret=max(ret,right-left-1);
    }
    cout<<ret<<endl;
    return 0;
}

二.游游的水果大礼包(二元一次方程组的求解问题)

1.题目: 游游的水果大礼包__牛客网

游游有n个苹果,m个桃子。她可以把2个苹果和1个桃子组成价值a元的一号水果大礼包,也可以把1个苹果和2个桃子组成价值b元的二号水果大礼包。游游想知道,自己最多能组成多少价值总和的大礼包?

2.算法原理:

利用枚举法从0开始假设可以组成x个1号大礼包,x的取值范围为[0,min(n/2,m)],定下来1号礼包的个数,那么可以计算出2号礼包的个数,y=min(n-2*x,(m-x)/2),则礼包总值为a*x+b*y,在枚举过程中更新总值的最大值

3.代码实现:

#include <iostream>
using namespace std;

int main() {
    long long n,m,a,b;
    cin>>n>>m>>a>>b;

    long long ret=0;
    for(long long x=0;x<=min(n/2,m);x++)//枚举1号礼包个数
    {
        long long y=min((n-2*x),(m-x)/2);//计算2号礼包个数
        ret=max(ret,a*x+b*y);
    }
    cout<<ret<<endl;

    return 0;
}

注:有些题目的数字类型要特别注意,否则可能会让测试结果就卡在70%左右非常恶心,建议对于可能取大值的数据一律定义long long类型,一了百了

三.两个链表的第一个公共节点

1.题目:两个链表的第一个公共结点_牛客题霸_牛客网

输入两个无环的单向链表,找出它们的第一个公共结点,如果没有公共节点则返回空。

2.算法原理:

<1>常规思路:统计出两个链表的长度,让长的链表先走两链表差值步,再和另一链表开始一起向后走,当第一次碰到相同的节点时,则该节点就是第一个公共节点

注意:有可能在长链表走链表差步时,就已经达到第一个公共节点处,此情况不要忘记考虑

<2>等量关系:让cur1,cur2分别指向两链表的头节点,当cur1走到空时,让cur1指向另一个链表的头节点;同理,当cur2走到空时,让cur2指向另一个链表的头节点,让cur1==cur2时,所指向的节点就是第一个公共节点。

class Solution {
public:
    ListNode* FindFirstCommonNode( ListNode* pHead1, ListNode* pHead2) {
		if(pHead1==nullptr || pHead2==nullptr) return nullptr;

		ListNode* cur1=pHead1;
		ListNode* cur2=pHead2;

		while(cur1!=cur2)
		{
			cur1=cur1==nullptr?pHead2:cur1->next;
			cur2=cur2==nullptr?pHead1:cur2->next;
		}
		return cur1;
        
    }
};

小小感慨一下,果然普通人是比不上天才的,这种思路绝对不是我这种普通人能想出来的🥲

四.Mari和shiny(多状态的动态规划问题)

1.题目:在一个字符串中找出"shy"的子序列

注:子串和子序列是不一样的,子串要求在原字符串中连续出现,但是子序列不要求连续出现,吐槽一句真恶心

2.算法原理:

利用数组统计在该字符前有多少个满足条件的选项。

创建shy数组统计该字符前有多少个“sh",若是str[i]=='y',则shy[i]=shy[i-1]+sh[i];若不等,则shy[i]=shy[i-1]

创建sh数组统计该字符前有多少个“s",若是str[i]=='h',则sh[i]=sh[i-1]+s[i];若不等,则sh[i]=sh[i-1]

创建s数组统计该字符前有多少个“s",若是str[i]=='s‘,则s[i]=s[i-1]+1;若不等,则s[i]=s[i-1]

注:可对本题做空间优化,即不创建数组直接利用三个变量统计

若str[i]=='s',则s++;若str[i]=='h',则h+=s;若str[i]=='y',则y+=h;

3.代码实现:

#include<iostream>
#include<string>
using namespace std;

int main()
{
	string str;
	cin >> str;

	int s = 0, h = 0, y = 0;
	for (int i = 0; i < str.size(); i++)
	{
		if (str[i] == 's') s++;
		else if (str[i] == 'h') h += s;
		else if (str[i] == 'y') y += h;
	}

	cout << y << endl;
	return 0;
}

zhu 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值