bzoj 3277: 串

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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: 串的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ3277 串

bzoj 3277 & bzoj 3473 串 —— 广义后缀自动机

bzoj 3277: 串

[BZOJ3277]串 广义后缀自动机

bzoj 3277: 串

Bzoj3277 串