bzoj 3277: 串
Posted 哈
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj 3277: 串相关的知识,希望对你有一定的参考价值。
以下全部是笔记,不要看了
注意:要求的不是"有多少不同的子串是...",相同的要重复计算贡献。
例如:
3 2
aca
a
c
答案是3 1 1第一个串中两个a都出现了两次,c出现了两次,所以第一个的答案是3
广义后缀自动机模板。
各个串连起来中间加分隔符的不方便,一般都要加很多特判的。。。。
有的说不定就不能用了。。
可以直接对着多个串建。就一句话:
把很多串的SAM建到了一个SAM上,建每个串的时候都从root开始(last=root)
(也可以在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: 串的主要内容,如果未能解决你的问题,请参考以下文章