pku 2541

 

题意:

 

在文本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;
} 

 

 

 

posted @ 2011-11-09 15:17  枕边梦  阅读(145)  评论(0编辑  收藏  举报