Distinct Substrings
寫完這題發現自己曾經的擴展KMP板子(函數)太laji了!現在的板子簡潔又漂亮,並且這題很妙!
題意:
給定一個長爲的數字串,問在尾部獨立的添加~這些數字分別會使原串增加多少本質不同的子串
思路:
- 看到本質不同,首先想到了後綴自動機,每次在結尾插入元素後只需要知道插入元素的父節點是誰,然後利用就可以知道增加了多少新的本質不同的子串
- 但就本題而言,有如下三點會否定這個做法:
- 空間,而後綴自動機要開很多的的數組
- 數組元素是數字,而不是字符,因此假設用存邊,空間會爆炸!
- 多組數據++大常數在兩秒內顯然是跑不出來的
- 否定後綴自動機後,我們仍然可以利用這個求新增子串的思想;在後綴自動機中,思考一個節點的節點是什麼?答案是與當前節點不同的最長的後綴,因此我們如果能夠在不建立後綴自動機的情況下找到這個最長的後綴,那不就好了?
- 而正好, 剛好有一種經典的做法,考慮將原字符串翻轉,就可以將求最長的後綴變成了求最長的前綴。並且要考慮分別添加個不同數字的情況下的子串增量,我們可以的計算貢獻,見如下式子:
其中表示在翻轉後的串的前面增加這個字符會有多少重複的子串出現,則就表示新增的本質不同的子串數量。這個旨在尋找最長的前綴 - 最後,此問題可以在的複雜度下解決,並且空間複雜度也完全(不過這題數據好像有些問題,導致暴力求數組也能過。。。)
#include "bits/stdc++.h"
#define hhh printf("hhh\n")
#define see(x) (cerr<<(#x)<<'='<<(x)<<endl)
using namespace std;
typedef long long ll;
typedef pair<int,int> pr;
inline int read() {int x=0;char c=getchar();while(c<'0'||c>'9')c=getchar();while(c>='0'&&c<='9')x=x*10+c-'0',c=getchar();return x;}
const int maxn = 1e6+10;
const int inf = 0x3f3f3f3f;
const int mod = 1e9+7;
const double eps = 1e-7;
int n, m;
int s[maxn], z[maxn], best[maxn];
int main() {
//ios::sync_with_stdio(false); cin.tie(0);
while(cin>>n>>m) {
for(int i=1; i<=n; ++i) scanf("%d", &s[i]);
reverse(s+1,s+1+n);
for(int i=2, j=1; i<=n; ++i) {
if(i<j+z[j]) z[i]=min(j+z[j]-i,z[i-j+1]);
while(i+z[i]<=n&&s[i+z[i]]==s[z[i]+1]) z[i]++;
if(i+z[i]>j+z[j]) j=i;
}
for(int i=1; i<=n; ++i) best[s[i]]=max(best[s[i]],1+z[i+1]);
ll ans=0, three=1;
for(int i=1; i<=m; ++i) {
three=three*3%mod;
ans^=three*(n+1-best[i])%mod;
}
cout<<ans<<endl;
for(int i=1; i<=max(n,m); ++i) best[i]=z[i]=0;
}
}