【GDOI2014】beyond

【GDOI2014】beyond

Description

有两个长度为n的字符串s,st,找出两个串前i个可以循环同构的最大长度。

 如abcdx,cdabx两个串,答案为4。s的第一到第二个字符于st的第三到第四的字符相等。

Solution

很容易想到在s中枚举一个分界点i左边的字符
然后,很容易想到求得一个exa[i]为st的s从i开始的字符串最长公共前缀的长度。求得一个exb[i]为s的st从i开始的字符串最长公共前缀的长度。
那么当枚举到分界点为i时,设exa[i+1]为l,那么st[1..l]与s[i+1..i+l]相等。
那么再在st中从1..l+1枚举一个j使得exb[j]+1>=i,那么就是已经循环同构了,那答案就是i+j-1。因为s[i+1..i+exa[i+1]]与st[1..exa[i+1]]相等,1<=j<=i+1,st[j+1..j+exb[j+1]]与s[1..exb[j+1]]相等,至少有s[1..i]=st[j..i+j-1],因为s[i+1..i+exa[i+1]]=st[1..j],exa[i+1]>j,所以至少有i+j-1
得到exa,exb自然用的是exkmp——>扩展kmp
O(n^2)
自然时间会超。
Optimization

使用并查集。
用f[i]存储一个exb[i+1]>=i节点,因为每次更新的是前缀的长度,当前前缀包含前面的前缀,所以可以向前递进,跟kmp的思想差不多。初始值自然是f[i]=i-1。
每次查询时,一直向上递进到一个exb[i+1]>=i的节点即可。
O(n);
Code

#include
#include
#include
#include
#include
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fod(i,a,b) for(i=a;i>=b;i--)
using namespace std;
int i,j,k,l,t,n,m,ans,p;
char s[2000005],st[2000005];
int nexta[2000005],exa[2000005],nextb[2000005],exb[2000005];
int f[2000005];
int gf(int x,int y){
    if(!x) return 0;
    if (exb[x+1]>=y) return x;else f[x]=gf(f[x],y);
}
int main(){
    scanf("%d",&n);
    scanf("%s",s+1);
    scanf("%s",st+1);
     nexta[1]=n;
    fo(i,2,n){
        p=k+nexta[k]-1;l=nexta[i-k+1];
        if(l+i>p){
            j=p-i+1;if(j<0)j=0;
            while(i+j<=n&&s[j+1]==s[i+j])j++;
            k=i;
            nexta[i]=j;
        }
        else{
            nexta[i]=l;
        }
    }
    k=0;
    fo(i,1,n){
        p=k+exa[k]-1;l=nexta[i-k+1];
        if(l+i>p){
            j=p-i+1;if(j<0)j=0;
            while(i+j<=n&&s[j+1]==st[i+j])j++;
            k=i;
            exa[i]=j;
        }
        else{
            exa[i]=l;
        }
    }
    nextb[1]=n;
    k=0;
    fo(i,2,n){
        p=k+nextb[k]-1;l=nextb[i-k+1];
        if(l+i>p){
            j=p-i+1;if(j<0)j=0;
            while(i+j<=n&&st[j+1]==st[i+j])j++;
            k=i;
            nextb[i]=j;
        }
        else{
            nextb[i]=l;
        }
    }
    k=0;
    fo(i,1,n){
        p=k+exb[k]-1;l=nextb[i-k+1];
        if(l+i>p){
            j=p-i+1;if(j<0)j=0;
            while(i+j<=n&&st[j+1]==s[i+j])j++;
            k=i;
            exb[i]=j;
        }
        else{
            exb[i]=l;
        }
    }
    fo(i,1,n) f[i]=i-1;
    fo(i,2,n-1){
        l=gf(exa[i+1],i);
        if(l) ans=max(ans,l+i);
    }
    printf("%d",ans);
}

你可能感兴趣的:(并查集,字符串,kmp,省选)