题意描述

核心:

总感觉题目描述有问题,把s1看出是字符, s1...sn组成一个字符串,h(s,c)表示字符s末尾添加一个字符c的时候,字串的数目;
增加的字串可以看成是以字符C结尾的后缀;s1..sn颠倒之后看出字符串(sn..s1)T串,对于CT串,我们求出CT与T各个位置的最大公共前缀,就得出了共有的字串部分;这个我们可以借助扩展kmp来求,nxt[i]表示T[i-(n-1)]与T之间公告前缀部分,求CT与T[i]的公共前缀,若T[i]为字符C, 则CT[i+1]与CT公共前缀是nxt[i+1] 

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define mod 1000000007
const int N=1e6+7;
int x[N],w[N],nxt[N];
int n,m;
void pre_exkmp()
{
    nxt[0]=n;
    int j=0;
    while(j+1<n&&x[j]==x[j+1])j++;
    nxt[1]=j;  // 从一开始,nxt[i-k]确实要前进
    int k=1;
    for(int i=2;i<n;i++)
    {
        int p=nxt[k]+k,t=nxt[i-k];
        if(i+t<p)nxt[i]=t;
        else 
        {
            j=p-i<0?0:p-i;
            while(i+j<n&&x[i+j]==x[j])j++;
            nxt[i]=j;k=i;
        }
    }
}
int main()
{
    while(~scanf("%d%d",&n,&m)) {
        for (int i=0;i<n;i++) scanf("%d",&x[n-i-1]);
        pre_exkmp();
        nxt[n]=0;
        for (int i=1;i<=m;i++) w[i]=-1; // 没有C字符,长度增加n+1
        // memset 在牛客是超时的啊
        for(int i=0;i<=n-1;++i) w[x[i]]=max(nxt[i+1],w[x[i]]);
        int ans=0,p=1;
        for(int i=1;i<=m;++i)
        {
            p=1ll*p*3%mod;
            ans^=1ll*p*(n-w[i])%mod;
        }
        printf("%d\n",ans);
    }
    return 0;
}