题意:
在文本T[1..n],找一个最长的后缀(长度为m,m<=13),以这个后缀为匹配的模式P:T[n-m+1..n]
找出P在文本T[1..n-1]出现的最后一个起始位置k,(k<=n-m)
若找到k,则得出T[n+1]为找到的模式T[k..k+m-1]的下一个位置: 即:T[n+1] = T[k+m]
如果m==13时找不到,则尝试m==12直到m==1,
如果都找不到,则T[n+1] = 0;
分析:
* KMP
* 一旦得到一个匹配之后就增加源串长度 下次找匹配 T 和 P 都会变
* 因为这次匹配之后 给源串增加的那个字符就是前面相匹配的串的下一个字符
* 所以至少这个位置会继续匹配下去
* 但是要求最右面的 长度又不大于 13 所以可能新构成的 P 在那个位置之后还有匹配
* ???
* 没有用新的 P 去找更右面的匹配 而是找到一个之后就确定输出解
*/
#include <cstring> #include <iostream> using namespace std; const int maxm = 14; const int maxl = 1000; const int maxn = 1000001; int m, n , s; char T[maxn+maxl], P[maxm]; int nxt[maxm]; int KMP(const char* T, const char* P) { nxt[0] = -1; int k = -1; for(int q=1;q<=m-1;q++){ while (k > -1 && P[k+1] != P[q]) k = nxt[k]; if (P[k+1] == P[q]) ++k; nxt[q] = k; } int pos = -1; int j = -1; while (s < n) { while (j > -1 && P[j+1] != T[s]) j = nxt[j]; if (P[j+1] == T[s]) ++j; if (j+1 == m) { pos = s + 1 - m; j = nxt[j];//题目要求找最右边的K,所以还要继续往右找 } ++s; } s = pos; return pos; } int main() { int N, L; scanf("%d %d\n", &N, &L); gets(T); s=0; for(int i=0;i<L;i++) { n = N - 1; bool find = false; for(int t=13;t>=1;t--) if (t < N) { m = t; memcpy(P, T+N-m, m); P[m] = '\0'; int pos = KMP(T, P); if (pos != -1) { printf("%c", T[N++] = T[pos+m]); find = true; break; } } if (!find) printf("%c", T[N++] = '0'); } printf("\n"); return 0; }