POJ - 2406 Power Strings(KMP/哈希)

传送门


本题需要 O ( n ) O(n) 求字符串的最小循环节,有两种方法:

哈希

根据哈希的性质,如果最小循环节的长度为 x x ,那么每 x x 长度的字符串的哈希值是相等的,那么我们只需预处理前 x , 1 x l e n x,1 \le x \le len 长度的哈希值以及 b a s e base 进制的 x , 1 x l e n x,1 \le x \le len 次方,然后根据哈希的如下性质便可以平均 l o g n logn 的复杂度计算出最小循环节:

h a s h ( x ) = h a s h ( 2 x ) h a s h ( x ) b n ( x ) = h a s h ( 3 x ) h a s h ( 2 x ) b n ( x ) hash(x)=hash(2x)-hash(x)*bn(x)=hash(3x)-hash(2x)*bn(x)

时间复杂度 O ( n l o g n ) O(nlogn)

char s[maxn];
ull base=131;
ull hashe[maxn],bn[maxn];

void init(){
    int n=strlen(s+1);
    hashe[0]=0,bn[0]=1;
    for(int i=1;i<=n;i++)
        hashe[i]=hashe[i-1]*base+(ull)s[i];
    for(int i=1;i<maxn;i++)
        bn[i]=bn[i-1]*base;
}

bool check(int x,int n){
    ull pre=hashe[x];
    for(int i=2*x;i<=n;i+=x){
        ull cur=hashe[i]-hashe[i-x]*bn[x];
        if(cur!=pre) return 0;
    }
    return 1;
}

void solve(){
    int n=strlen(s+1);
    for(int i=1;i<=n;i++){
        if(n%i==0 && check(i,n)){
            printf("%d\n",n/i);
            break;
        }
    }
}

KMP的next数组

实际上next数组解决的最大前缀后缀匹配长度,如果一个串是循环字符串,那么一定有 l e n % ( n n e x t [ l e n ] ) = = 0 len\%(n-next[len])==0 ,例如:

a b c a b c a b c abcabcabc

      a b c a b c a b c ~~~~~abcabcabc

int Next[maxn];

void initNext(char *p){
    int len=strlen(p);
    int k=-1,j=0;
    Next[0]=-1;
    while(j<len){
        if(k==-1 || p[j]==p[k]){
            Next[++j]=++k;
        }else k=Next[k];
    }
}

int solve(char *s){
    initNext(s);
    int n=strlen(s);
	if(n%(n-Next[n])==0) return n/(n-Next[n]);
    return 1;
}

猜你喜欢

转载自blog.csdn.net/qq_44691917/article/details/107566161