標籤:max with fir 建立 class add def local mes
題意:給出多個串s1…sn,每個串si對應happy值hi,對於某個串t,如果它在串si,sj,sk..等串中出現,那麼串t的happy值為hi * hj * hk * …,詢問一個m,輸出長度<=m的隨機串的happy值期望。
先來一種暴力做法,遍曆s1…sn的所有子串,對於某個子串t,算出它對ans(len(t))的貢獻,不重複地累加所有子串貢獻,最後取首碼和即可。
回顧下sam的每個狀態(點),它代表了多個串,這多個串的結束位置集相同,且如果按照長度將他們排序,那它們長度遞增且每個串都是這些串中長度最大的串的尾碼,如bbc,bbcd,bbcdc,這點其實好理解,因為最大長度的那個串在某個位置k結束,那它的所有尾碼也都會在k出現,那為什麼不是從長度1到長度max呢,因為長度小的串可能在別的地方也出現過,也就是說一個串的出現位置集大小總是小於等於它的尾碼的出現位置集大小。
回到這題,再下看廣義sam做法,先說建立了廣義sam後怎麼做,此時每個子串都在sam中,在單個串的sam中,每個點代表的串有相同的結束位置集,那在廣義sam裡也有相同的結束位置集,且這些串在哪些串中出現也是一樣的,所以這些串的happy值是一樣的,那一個點的happy值就是累乘出現串集的所有h值。
那如何建立廣義sam?對一個串si建立sam時,從初始狀態開始轉移,當要加入字元c = s[i][j]時,如果發現目前狀態st已經有邊c指向下一狀態v,且v代表的串的最大長度=當前已匹配串長度,那狀態v可以代表當前串s[i][0]…s[i][j],否則建立結點(不會證)。
那建完廣義sam如何維護happy值?每個串跑一遍,遇到的點都維護上權值,並且要沿著fail邊跑回去更新,每個點只更新一次。
接下來只要遍曆所有點統計答案就行了,因為一個點代表的多個串的長度是連續的,所以再區間覆蓋一下即可。
#include<iostream>#include<cmath>#include<cstring>#include<queue>#include<vector>#include<cstdio>#include<algorithm>#include<map>#include<set>#define rep(i,e) for(int i=0;i<(e);i++)#define rep1(i,e) for(int i=1;i<=(e);i++)#define repx(i,x,e) for(int i=(x);i<=(e);i++)#define pii pair<int,int>#define X first#define Y second#define PB push_back#define MP make_pair#define mset(var,val) memset(var,val,sizeof(var))#define scd(a) scanf("%d",&a)#define scdd(a,b) scanf("%d%d",&a,&b)#define scddd(a,b,c) scanf("%d%d%d",&a,&b,&c)#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)using namespace std;typedef long long ll;template <class T>void test(T a){cout<<a<<endl;}template <class T,class T2>void test(T a,T2 b){cout<<a<<" "<<b<<endl;}template <class T,class T2,class T3>void test(T a,T2 b,T3 c){cout<<a<<" "<<b<<" "<<c<<endl;}const int N = 1e6+10;const int inf = 0x3f3f3f3f;const ll INF = 0x3f3f3f3f3f3f3f3f;const int mod = 1e9+7;int idx;int maxlen[N], minlen[N], trans[N][26], slink[N];int new_state(int _maxlen, int _minlen, int* _trans, int _slink) { maxlen[idx] = _maxlen; minlen[idx] = _minlen; for(int i = 0; i < 26; i++) { if(_trans == NULL) trans[idx][i] = -1; else trans[idx][i] = _trans[i]; } slink[idx] = _slink; return idx++;}int val[N];int add_char(char ch, int u) { int c = ch - ‘a‘; int z = new_state(maxlen[u] + 1, -1, NULL, -1); while(u != -1 && trans[u][c] == -1) { trans[u][c] = z; u = slink[u]; } if(u == -1) { minlen[z] = 1; slink[z] = 0; return z; } int x = trans[u][c]; if(maxlen[u] + 1 == maxlen[x]) { minlen[z] = maxlen[x] + 1; slink[z] = x; return z; } int y = new_state(maxlen[u] + 1, -1, trans[x], slink[x]); minlen[z] = minlen[x] = maxlen[y] + 1; slink[z] = slink[x] = y; while(u != -1 && trans[u][c] == x) { trans[u][c] = y; u = slink[u]; } minlen[y] = maxlen[slink[y]] + 1; return z;}string s[N];int h[N];int wlk[N];ll qpow(ll a,ll b){ ll ret = 1; while(b){ if(b&1)ret=ret*a%mod; b>>=1; a=a*a%mod; } return ret;}ll INV(ll a){ return qpow(a,mod-2);}int ans[N];int vis[N];void work(){ int n;cin>>n; int mxlen = 0; rep(i,n){ cin>>s[i]; mxlen = max(mxlen, (int)s[i].size()); } new_state(0,0,NULL,-1); rep(i,n){ cin>>h[i]; int st=0; rep(j,s[i].size()){ int nx = trans[st][s[i][j]-‘a‘]; if(nx == -1 || maxlen[nx] != j+1){//建廣義sam,兩種情況 st=add_char(s[i][j], st); }else{ st = nx; } } } rep(i,n){ int st=0; rep(j,s[i].size()){ st = trans[st][s[i][j]-‘a‘]; int u = st; while(u!=-1){//遍曆所有和該串有關的點 if(vis[u]==i+1) break; // 不memset的vis vis[u]=i+1; if(!val[u]) val[u]=h[i]; else val[u]=1ll*val[u]*h[i]%mod; u=slink[u]; } } } rep1(i,idx-1){ (wlk[minlen[i]] += val[i])%=mod; (wlk[maxlen[i]+1] -= val[i])%=mod;//區間覆蓋 } int now = 0; int sum = 0; int div = 0; int pw = 1; rep1(i,mxlen){ pw=pw*26ll%mod; (div+=pw)%=mod; (now += wlk[i])%=mod; (sum+=now)%=mod; ans[i] = 1ll*sum*INV(div)%mod; } int m;cin>>m; rep(i,m){ int x;cin>>x; if(x>mxlen) x = mxlen;//記得特判 test((ans[x]+mod)%mod); }}int main() { #ifdef local freopen("in.txt","r",stdin); #endif // local IOS; work();}
HDU6405 Make ZYB Happy 廣義sam