bzoj 3277: 串
以下全部是筆記,不要看了
註意:要求的不是"有多少不同的子串是...",相同的要重復計算貢獻。
例如:
3 2
aca
a
c
答案是3 1 1第一個串中兩個a都出現了兩次,c出現了兩次,所以第一個的答案是3
廣義後綴自動機模板。
各個串連起來中間加分隔符的不方便,一般都要加很多特判的。。。。
有的說不定就不能用了。。
可以直接對著多個串建。就一句話:
把很多串的SAM建到了一個SAM上,建每個串的時候都從root開始(last=root)
https://www.cnblogs.com/candy99/p/6374177.html
http://dwjshift.logdown.com/posts/304570
(也可以在trie上建,大概就是每個節點建時last就是父親建完的np?沒試過)
這樣子復雜度可以證明是O(G(T))(好像要乘上字符集大小?沒確定),G(T)為Trie樹上所有葉子節點深度和,一定不超過所有串長度和
這樣子插入的時候要判斷是否已經存在對應節點,如果存在則考慮直接復用或者拆開後復用(就是普通的後綴自動機構建頭上加一點跟後面很像的東西)
建出後綴樹後,對於每一個節點維護一個集合,表示這個點到根的路徑表示的後綴屬於哪些字符串
對於每個節點計算貢獻(一開始不直接將答案更新到對應字符串上,只更新到節點上,曾經陷入誤區),就是如果它到根表示的後綴出現超過一次(就是以它為根的子樹中各個集合的並集的size>1),那麽產生len[t]-len[par[t]]的貢獻,否則不產生貢獻
最後還要一遍dfs把各個節點的答案加到以其為根子樹中各個節點的答案上(將每個後綴的實際貢獻更新為其各個前綴的貢獻之和,達到求一個字符串所有子串貢獻的目的)
這個地方我用了啟發式合並來統計不同子串數量,好像有轉換到序列上用樹狀數組做的方法。。。
最後每個串的答案就是其每個位置對應的後綴的答案之和
上面基本全是假的看不懂的。。。。還是意識流吧
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #include<queue> 5#include<vector> 6 #include<set> 7 using namespace std; 8 int n,x; 9 char ss[100100],*sp=ss,*pp[100100]; 10 int le[100100]; 11 vector<int> tt[100100]; 12 namespace SAM 13 { 14 int mem,root; 15 int len[300100],par[300100]; 16 int trans[300100][26]; 17 void append(int ch,int& np) 18 { 19 int p=np; 20 //如果已經存在倒著的原串首部加上ch的串 21 if(trans[p][ch]) 22 { 23 int q=trans[p][ch]; 24 if(len[q]==len[p]+1)//如果沒有被壓過 25 np=trans[np][ch]; 26 else//如果被壓了,要拆開 27 { 28 np=++mem;len[np]=len[p]+1; 29 par[np]=par[q];par[q]=np; 30 memcpy(trans[np],trans[q],sizeof(trans[np])); 31 for(;p&&trans[p][ch]==q;p=par[p]) trans[p][ch]=np; 32 } 33 return; 34 } 35 np=++mem;len[np]=len[p]+1; 36 for(;p&&!trans[p][ch];p=par[p]) trans[p][ch]=np; 37 if(!p) par[np]=root; 38 else 39 { 40 int q=trans[p][ch]; 41 if(len[q]==len[p]+1) par[np]=q; 42 else 43 { 44 int nq=++mem;par[nq]=par[q];par[q]=par[np]=nq; 45 memcpy(trans[nq],trans[q],sizeof(trans[nq]));len[nq]=len[p]+1; 46 for(;p&&trans[p][ch]==q;p=par[p]) trans[p][ch]=nq; 47 } 48 } 49 } 50 set<int> ss[301000]; 51 int in[300100]; 52 long long ans[300100]; 53 queue<int> q; 54 vector<int> son[300100]; 55 void dfs(int u) 56 { 57 for(int i=0;i<son[u].size();i++) 58 { 59 ans[son[u][i]]+=ans[u]; 60 dfs(son[u][i]); 61 } 62 } 63 void work() 64 { 65 int i,t; 66 for(i=1;i<=mem;i++) in[par[i]]++; 67 for(i=1;i<=mem;i++) 68 if(!in[i]) 69 q.push(i); 70 set<int>::iterator it; 71 while(!q.empty()) 72 { 73 t=q.front();q.pop(); 74 if(!t) continue; 75 if(ss[t].size()>=x) ans[t]=(len[t]-len[par[t]]); 76 if(ss[par[t]].size()<ss[t].size()) swap(ss[par[t]],ss[t]); 77 for(it=ss[t].begin();it!=ss[t].end();++it) 78 ss[par[t]].insert(*it); 79 in[par[t]]--; 80 if(in[par[t]]==0) q.push(par[t]); 81 } 82 for(i=1;i<=mem;i++) son[par[i]].push_back(i); 83 dfs(root); 84 } 85 } 86 long long anss; 87 88 int main() 89 { 90 int i,j,np;char *b,*ed; 91 scanf("%d%d",&n,&x); 92 SAM::root=++SAM::mem; 93 for(i=1;i<=n;i++) 94 { 95 scanf("%s",sp); 96 pp[i]=sp;le[i]=strlen(sp);sp+=le[i]; 97 np=SAM::root;b=pp[i];ed=pp[i]+le[i]; 98 for(;b!=ed;b++) 99 { 100 SAM::append(*b-‘a‘,np); 101 SAM::ss[np].insert(i); 102 tt[i].push_back(np); 103 } 104 } 105 SAM::work(); 106 for(i=1;i<=n;i++) 107 { 108 anss=0; 109 for(j=0;j<tt[i].size();j++) anss+=SAM::ans[tt[i][j]]; 110 printf("%lld ",anss); 111 } 112 return 0; 113 }
bzoj 3277: 串