一个比较详细的博客https://www.cnblogs.com/love-yh/p/7072161.html
马拉车思想中感觉有点dp,还是觉得每次对每一个中心点计算太麻烦,耗时,所以想让已有的数据对对计算提供帮助。这里面利用了回文字符串的特点,回文。
具体的介绍我也不可能写的有那个博客好,我补充一下代码实现,其他你们去看博客就好了。
牛客题https://ac.nowcoder.com/acm/problem/14517,来试试吧!
当然下面的代码过不了这道题,需要在主函数哪里修改一下!| ^ _^|
具体代码:
#include<cstdio>
#include<iostream>
#include<string>
using namespace std;string st;//加工后的字符串储存位置
char ch[2000];//未加工字符串储存位置 ,也可以使用string取代
int num[2000];//记录每个中心点对称半径的位置,可使用vector替代
int min(int x,int y){//出较小值,可以用库函数math取代
return x>y?y:x;}
int gai(){//对字符串进行转制,每个字符间添加#,可以是其他的,但是首尾必须和其他字符不同
int i=0;
st+='$';
st+='#';
for(int i=0;ch[i]!='\0';i++){
st+=ch[i];
st+='#';
}
st+='\0';
return 0;
}
int chu(){
int len=0,right=0;//len是中心点下标,right是len对称的最后一个字符的下标
num[0]=0;//第一个为零,添加的是$,纪录的对称半径不包括中心点,为了对应原始字符串的对称长度
int max=0;//最大值
for(int i=1;i!=st.size();i++){
num[i]=i<right?min(num[len*2-i],right-len):0;
while(st[i+num[i]+1]==st[i-num[i]-1])num[i]++;//第三种情况
if(num[i]>right-len){//更新数值
len=i;
right=i+num[i];
max=num[i];
}
}
return max;//传递最大数,也可以得到最大串是哪个,我们有中心点和半径
}
int main(){
scanf("%s",ch);
gai();
int a=chu();
printf("the first long words is long %d.\n",a);//输出
return 0;
}
自我感觉如果字符串不是很长,对称部分也不长的话,马拉车的算法复杂度接近n的平方,甚至耗时更长。倒叙求法和原始未优化的的算法还是要学会实现比较好。
但是对于求解最长字符串的问题,不只是马拉车,还有一种方法,也可以做到,那就是dp,具体哪一种方法更有,自己体会吧!
dp使用的思想也是利用已经计算过的数据,为下次计算提供方便,其中的状态转移方程是dp[i][j]={dp[i+1][j-1] ch[i]==ch[j]; 0 ch[i]!=ch[j]}其中dp[i][j]的意思是区间i到j是否为回文串,是的话为1,不是为0,也可以让等与回文串长度,这种方法的复杂度为n方。
#include<cstdio>
#include<cstring>
#include<string>
#include<iostream>
using namespace std;
string s;//接收字符串
int dp[1000][1000];//储存
int main(){
cin>>s;
int len=s.size(),ans=1;
memset(dp,0,sizeof(dp));//初始化
for(int i=0;i<len;i++){//让长度为一和二的提前算出,作为基础
dp[i][i]=1;
if(i<len-1&&s[i]==s[i+1]){
dp[i][i+1]=1;
ans=2;
}
}
for(int lon=2;lon<len;lon++){
for(int i=0;i+lon<len;i++){
if(s[i]==s[i+lon]&&dp[i+1][i+lon-1]==1){
dp[i][i+lon]=dp[i+1][i+lon-1];//区间转移,不需要赋值0是因为初始化都为零
ans=lon+1;//不需要比大小是因为我们是从小到大计算是否是回问串的,所以后来的一定比开始的长
}
}
}
printf("%d\n",ans);//输出最长长度,也可以标点记录位置
return 0;
}
其中代码是来自算法笔记中的动态规划,复杂度为n^2。