2019HNCPC Distinct Substrings (扩展KMP)

2019HNCPC Distinct Substrings

Mean

给定一个\(n\)个整数的字符串,定义\(f(s_1,s_2,...,s_n)\)为本质不同的子串个数,

对于每个整数\(c∈[1,m]\),分别输出\(f(s_1,s_2,...,s_n)-f(s_1,s_2,...,s_n,c)\).

\(n,m<=1e6\)

Sol

扩展\(kmp\).

似乎是个经典做法?

参考OIWKI

给定一个长度为 \(n\)的字符串\(s\),计算\(s\)的本质不同子串的数目。

考虑计算增量,即在知道当前\(s\)的本质不同子串数的情况下,计算出在\(s\)末尾添加一个字符后的本质不同子串数。

\(k\)为当前\(s\)的本质不同子串数。我们添加一个新的字符\(c\)\(s\)的末尾。显然,会出现一些以\(c\)结尾的新的子串(以 \(c\)结尾且之前未出现过的子串)。

设串 \(t\)\(s+c\)的反串(反串指将原字符串的字符倒序排列形成的字符串)。我们的任务是计算有多少\(t\)的前缀未在\(t\) 的其他地方出现。考虑计算 \(t\)\(Z\) 函数并找到其最大值 \(zmax\)。则 \(t\) 的长度小于等于 \(zmax\) 的前缀的反串在 \(s\) 中是已经出现过的以\(c\)结尾的子串。

所以,将字符\(c\) 添加至 \(s\) 后新出现的子串数目为 \(|t|-zmax\)

算法时间复杂度为\(O(n^2)\)

值得注意的是,我们可以用同样的方法在 \(O(n)\)时间内,重新计算在端点处添加一个字符或者删除一个字符(从尾或者头)后的本质不同子串数目。

按照上述做法,先将\((s_1,s_2,...,s_n)\)翻转成\((s_n,s_{n-1},...,s_1)\),然后求一下\(Z\)函数。

最后考虑若加入\(c\),在对于所有\((s_n,s_{n-1},...,s_1)\)中,\(s_i=c\),则需要将\(z_{i+1}\)代入\(c\)的情况取\(Max\),开一个桶维护一下即可。

剩下的就是如上的贡献计算了,具体看代码。

Code

#include<bits/stdc++.h>
using namespace std;
const int N = 1e6+10;
int mx[N];
typedef long long ll;
const ll mod = 1e9+7;
// C++ Version
int a[N];
int s[N];
void z_function(int s[],int n) {
   
  for (int i = 1, l = 0, r = 0; i < n; ++i) {
    if (i <= r && a[i - l] < r - i + 1) {
      a[i] = a[i - l];
    } else {
      a[i] = max(0, r - i + 1);
      while (i + a[i] < n && s[a[i]] == s[i + a[i]]) ++a[i];
    }
    if (i + a[i] - 1 > r) l = i, r = i + a[i] - 1;
  }
}

int n,m;
int main(){
    //freopen("in.txt","r",stdin);
    while(~scanf("%d%d",&n,&m)){
        int maxx=0;
        for(int i=0;i<n;++i){
         
            scanf("%d",&s[i]);
            maxx=max(maxx,s[i]);
            a[i]=0;
        }
        for(int i=0;i<=m;++i){
            mx[i]=0;
        }
        reverse(s,s+n);

        z_function(s,n);
        

        for(int i=0;i<n-1;++i){
            mx[s[i]]=max(mx[s[i]],1+a[i+1]);
        }
        mx[s[n-1]]=max(mx[s[n-1]],1);
        ll ans=0;
        ll jc=1;
        for(int i=1;i<=m;++i){
            jc = jc*3%mod;
            if(mx[i]==0){
                ans^= ((n+1)*jc%mod);
            }
            else{
                ans^= (((n+1)-mx[i])*jc%mod);
            }
            
        }
        printf("%lld\n",ans);
    }

    return 0;
}

posted @ 2021-10-25 17:51  Qquun  阅读(49)  评论(0)    收藏  举报