逆序KMP,真的是强大!
参考链接,下面有题意解释:
http://blog.sina.com.cn/s/blog_6ec5c2d00100tphp.html
http://blog.csdn.net/sdjzping/article/details/8857749
http://tech.ddvip.com/2013-09/1380477442203505.html
直接暴力是枚举字符串的后面13个的字母,然后再用KMP匹配,这样的话,就绪要枚举多次,分别是后面的13,12,11....1个字母。
但是通过观察可以发现,其实要求的是最长公共后缀! 那么可以把原来的字符串逆序转换一下,就变成了求最长公共前缀!
这样只需要求一次,用字符串的前面13个字母和原字符串从第二个字符开始进行匹配一次就够了!
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#include <iostream> #include <stdio.h> #include <algorithm> #include <string> #include <string.h> using namespace std; const int maxn=1000000+1010; char s[maxn],str[maxn]; int next[maxn]; char ans[1005]; //答案 int n,l; int start=1005; //标记字符串的起始位置 int k; void getNext(char*str) { next[1]=0; int k=0; int lm=strlen(str); for(int i=1; i<lm; i++) { while(k&&str[k]!=str[i]) k=next[k]; if(str[i]==str[k]) k++; next[i+1]=k; } } //逆序KMP,转化为求最长公共前缀 int kmp(char*P,int len) { int k=0,c=0,idx; int lm=strlen(P); for(int i=start+1; i<start+len; i++) { while(k&&P[k]!=str[i]) k=next[k]; if(P[k]==str[i]) k++; if(k>c){ c=k; idx=i; } if(k==lm) return i-k; } if(c) return idx-c; else return -1; } int main() { char tmp[20]; scanf("%d%d",&n,&l); scanf("%s",&s); int len=strlen(s); for(int i=0;s[i];i++){ str[start+i]=s[len-1-i]; } str[start+len]='\0'; int cnt=l,k; while(cnt--){ //截取开始的13个字符 strncpy(tmp,str+start,13); if(len<13) tmp[len]='\0'; else tmp[13]='\0'; getNext(tmp); k=kmp(tmp,len); start--; len++; if(k==-1) str[start]='0'; //不存在匹配的情况 else str[start]=str[k]; } for(int i=0;i<l;i++) ans[i]=str[start+l-1-i]; ans[l]='\0'; printf("%s\n",ans); return 0; }