HDU 4763 Theme Section (KMP)

先KMP求出f数组。


然后令j等于f[len],在j沿着失配边走到0的路径上(即一直j=f[j]),每个节点都代表了一对相同的前后缀,且长度为f[j]。

这些对前后缀都可能是答案子串,再用kmp算法在中间找这个子串存不存在即可。

注意,子串长度会越来越小,而且一定包含于之前判断过的子串中(因为起点一样)。所以若前一个子串在l到r区间不存在,那么下一个子串也一定不存在。这样记录已扫描区间的范围,只去看这个范围外的即可。保证每个位置只被查过一次,复杂度O(N)。


代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

char s[1000005];
int f[1000005];
void getfail(){
    f[0]=f[1]=0;
    int len=strlen(s);
    for(int i=1;i<len;i++){
        int j=f[i];
        while(j&&s[i]!=s[j]) j=f[j];
        f[i+1]=(s[i]==s[j]? j+1:0);
    }
}


int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%s",&s);
        int len=strlen(s);
        getfail();
        int l=f[len];
        int r=len-l;
        while(l>r){
            l=f[l];
            r=len-r;
        }
        int sl=r,sr=l;
        bool ok=0;
        int res=0;

        while(1){
            int j=0;
            for(int i=l;i<sl;i++){
                while(j&&s[j]!=s[i]) j=f[j];
                if(s[j]==s[i]) j++;
                if(j==l){
                    ok=1;
                    break;
                }
            }
            j=0;
            for(int i=sr;i<r;i++){
                while(j&&s[j]!=s[i]) j=f[j];
                if(s[j]==s[i]) j++;
                if(j==l){
                    ok=1;
                    break;
                }
            }
            if(ok){
                res=l;
                break;
            }
            sl=l;
            sr=r;
            l=f[l];
            r=len-l;
            if(!l){
                res=0; break;
            }
        }
        printf("%d\n",res);
    }
    return 0;
}


你可能感兴趣的:(KMP)