Minimum Index(所有前缀的最小后缀 Lyndon分解 KMP 最短后缀匹配)

https://ac.nowcoder.com/acm/contest/5669/I

题意:

就是求每个前缀的最小后缀,答案是用最小位置算

解析:

先lyndon分解,显然最后的lyndon串的字典序最小。

在最后lyndon串内部,用KMP判断。

当后缀等于前缀时,即当 p r e . . . p r e pre...pre pre...pre时,pre为最小字典序。

这个用KMP求,但是KMP求的是最长,我们求的是最短( a a . . . a a aa...aa aa...aaKMP得到aa,而实际最小是a)。这个处理办法是对aa再次跳fail,这个过程用记忆化加速。

代码:

/*
 *  Author : Jk_Chen
 *    Date : 2020-07-21-15.03.56
 */
#include
using namespace std;
#define LL long long
#define rep(i,a,b) for(int i=(int)(a);i<=(int)(b);i++)
#define per(i,a,b) for(int i=(int)(a);i>=(int)(b);i--)
#define mmm(a,b) memset(a,b,sizeof(a))
#define pb push_back
#define pill pair
#define fi first
#define se second
void test(){cerr<<"\n";}
template<typename T,typename... Args>void test(T x,Args... args){cerr<<"> "<<x<<" ";test(args...);}
const LL mod=1e9+7;
const int maxn=1e6+9;
const int inf=0x3f3f3f3f;
LL rd(){ LL ans=0; char last=' ',ch=getchar();
    while(!(ch>='0' && ch<='9'))last=ch,ch=getchar();
    while(ch>='0' && ch<='9')ans=ans*10+ch-'0',ch=getchar();
    if(last=='-')ans=-ans; return ans;
}
#define rd rd()
/*_________________________________________________________begin*/


vector<int>Pos;
void deal(char *x,int len){
    int i=0;
    while(i<len){
        int j=i,k=i+1;
        while(k<len&&x[j]<=x[k]){
            if(x[j]==x[k])
                j++;
            else
                j=i;
            k++;
        }
        while(i<=j){
            i+=k-j;
            Pos.push_back(i);
        }
    }
}

int fail[maxn];
void KMP(char *x,int *fail,int L,int R){
//    int i=L,j=-1;
//    fail[0]=-1;
//    while(i<=R){
//        while(j!=-1&&x[i]!=x[j]) j=fail[j];
//        fail[++i]=++j;
//        if(x[i]==x[j]) fail[i]=fail[j];
//    }

    fail[L]=-1,fail[L+1]=L;
    int i=L+1,j=L;
    while(i<=R&&j<=R){
        if(j==-1||x[i]==x[j]){
            if(j==-1)
                fail[++i]=j=L;
            else
                fail[++i]=++j;
        }
        else j=fail[j];
    }
}


char x[maxn];
LL ans[maxn];
// every substring's endpos , index begin at 1

int F[maxn];
int Find(int p,int L){
    if(F[p]!=-1)return F[p];
    if(fail[p+1]>L){
        int len=Find(fail[p+1]-1,L);
        if(len==inf)
            return F[p]=fail[p+1]-L;
        else
            return F[p]=len;
    }
    else{
        return F[p]=inf;
    }
}

int main(){
    int _=rd;
    while(_--){
        Pos.clear();
        scanf("%s",x);
        int len=strlen(x);
        rep(i,0,len)F[i]=-1;
        deal(x,len);

        int L=0,R;
        for(int i=0;i<Pos.size();i++){
            R=Pos[i]-1;
#ifdef TEST
            test(L,R);
#endif // TEST
            KMP(x,fail,L,R);
            rep(j,L,R){
                int res=Find(j,L);
                if(res!=inf)
                    ans[j]=j-res+1;
                else
                    ans[j]=L;
            }
            L=Pos[i];
        }

        LL A=0,B=1;
        rep(i,0,len-1){
#ifdef TEST
            test(ans[i]+1);
#endif // TEST
            A=(A+B*ans[i]+B)%mod;
            B=B*1112%mod;
        }
        printf("%lld\n",A);
    }
    return 0;
}

/*
100
abababbacbbacbaccbaaacaabcac

4
bbaabaaab
baacbaacb
aabaabaabc
abcabcabc
*/

你可能感兴趣的:(字符串)