1. 程式人生 > 其它 >2019HNCPC Distinct Substrings (擴充套件KMP)

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;
}