codeforces E. Little Elephant and Strings(广义后缀自动机,Parent树)

传送门在这里。

大意:

  给一堆字符串,询问每个字符串有多少子串在所有字符串中出现K次以上。

解题思路:

  这种子串问题一定要见后缀自动机Parent树Dfs序统计出现次数都是套路了吧。

这道题统计子串个数,那么可以发现,若一个节点所对应的子串出现了K次,那么其贡献就是len,不需要考虑重复。

因为即使出现重复也是在两个位置。

那么只需统计以每个点结束的子串就好了。

之前的Dfs序就很套路了。

只需再跑一遍字符串,更新答案就好了。

代码:

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<algorithm>
  4 const int N=400000;
  5 struct sant{
  6     int tranc[26];
  7     int len;
  8     int pre;
  9 }s[N];
 10 struct pnt{
 11     int hd;
 12     int ind;
 13     int oud;
 14     int col;
 15     int ans;
 16 }p[N];
 17 struct ent{
 18     int twd;
 19     int lst;
 20 }e[N];
 21 struct int_2{
 22     int l;
 23     int r;
 24     int no;
 25 }d[N];
 26 int n,k;
 27 int siz;
 28 int dfn;
 29 int cnt;
 30 int fin;
 31 char tmp[N];
 32 int ll[N],rr[N];
 33 int col[N];
 34 int lst[N];
 35 int line[N];
 36 int str[N];
 37 int lowbit(int x)
 38 {
 39     return x&(-x);
 40 }
 41 void update(int pos,int x)
 42 {
 43     while(pos&&pos<=dfn)
 44     {
 45         line[pos]+=x;
 46         pos+=lowbit(pos);
 47     }
 48     return ;
 49 }
 50 int query(int pos)
 51 {
 52     int ans=0;
 53     while(pos)
 54     {
 55         ans+=line[pos];
 56         pos-=lowbit(pos);
 57     }
 58     return ans;
 59 }
 60 bool cmp(int_2 x,int_2 y)
 61 {
 62     return x.r<y.r;
 63 }
 64 void ade(int f,int t)
 65 {
 66     cnt++;
 67     e[cnt].twd=t;
 68     e[cnt].lst=p[f].hd;
 69     p[f].hd=cnt;
 70     return ;
 71 }
 72 void Insert(int c,int pl)
 73 {
 74     int nwp,nwq,lsp,lsq;
 75     nwp=++siz;
 76     s[nwp].len=s[fin].len+1;
 77     p[nwp].col=pl;
 78     for(lsp=fin;lsp&&!s[lsp].tranc[c];lsp=s[lsp].pre)
 79         s[lsp].tranc[c]=nwp;
 80     if(!lsp)
 81         s[nwp].pre=1;
 82     else{
 83         lsq=s[lsp].tranc[c];
 84         if(s[lsq].len==s[lsp].len+1)
 85             s[nwp].pre=lsq;
 86         else{
 87             nwq=++siz;
 88             s[nwq]=s[lsq];
 89             s[nwq].len=s[lsp].len+1;
 90             s[lsq].pre=s[nwp].pre=nwq;
 91             while(s[lsp].tranc[c]==lsq)
 92             {
 93                 s[lsp].tranc[c]=nwq;
 94                 lsp=s[lsp].pre;
 95             }
 96         }
 97     }
 98     fin=nwp;
 99 }
100 void Dfs(int x)
101 {
102     p[x].ind=++dfn;
103     col[dfn]=p[x].col;
104     for(int i=p[x].hd;i;i=e[i].lst)
105     {
106         int to=e[i].twd;
107         Dfs(to);
108     }
109     p[x].oud=++dfn;
110     col[dfn]=p[x].col;
111 }
112 int main()
113 {
114     scanf("%d%d",&n,&k);
115     if(k>n)
116     {
117         for(int i=1;i<=n;i++)
118             printf("%d ",0);
119         return 0;
120     }
121     fin=++siz;
122     for(int i=1;i<=n;i++)
123     {
124         ll[i]=rr[i-1]+1;
125         rr[i]=rr[i-1];
126         fin=1;
127         scanf("%s",tmp);
128         int len=strlen(tmp);
129         for(int j=0;j<len;j++)
130             str[++rr[i]]=tmp[j]-‘a‘;
131         for(int j=ll[i];j<=rr[i];j++)
132         {
133             Insert(str[j],i);
134         }
135     }
136
137     for(int i=2;i<=siz;i++)
138         ade(s[i].pre,i);
139     Dfs(1);
140     for(int i=1;i<=siz;i++)
141         d[i]=(int_2){p[i].ind,p[i].oud,i};
142     std::sort(d+1,d+siz+1,cmp);
143     int r=1;
144     for(int i=1;i<=siz;i++)
145     {
146         while(r<=d[i].r)
147         {
148             if(!col[r])
149             {
150                 r++;
151                 continue;
152             }
153             if(lst[col[r]])
154                 update(lst[col[r]],-1);
155             update(r,1);
156             lst[col[r]]=r;
157             r++;
158         }
159         r--;
160         p[d[i].no].ans=query(d[i].r)-query(d[i].l-1);
161     }
162     for(int i=1;i<=n;i++)
163     {
164         long long int ans=0;
165         int root=1;
166         for(int j=ll[i];j<=rr[i];j++)
167         {
168             root=s[root].tranc[str[j]];
169             while(p[root].ans<k)
170                 root=s[root].pre;
171             ans+=(long long)s[root].len;
172         }
173         printf("%I64d ",ans);
174     }
175     puts("");
176     return 0;
177 }

原文地址:https://www.cnblogs.com/blog-Dr-J/p/10083876.html

时间: 2024-11-10 00:01:17

codeforces E. Little Elephant and Strings(广义后缀自动机,Parent树)的相关文章

CodeForces-204E:Little Elephant and Strings (后缀自动机)

The Little Elephant loves strings very much. He has an array a from n strings, consisting of lowercase English letters. Let's number the elements of the array from 1 to n, then let's denote the element number i as ai. For each string ai (1 ≤ i ≤ n) t

E. Three strings 广义后缀自动机

http://codeforces.com/problemset/problem/452/E 多个主串的模型. 建立一个广义后缀自动机,可以dp出每个状态的endpos集合大小.同时也维护一个R[]表示那个串出现过. 所以可以算出每个状态的dp[i][k]表示第k个串在第i个状态中出现的次数. 可以知道sigma dp[i][0...k]是等于  endpos集合的大小. 然后把这个贡献加到min(i)....max(i)中去就可以了 差分一下. #include <bits/stdc++.h>

CF.666E.Forensic Examination(广义后缀自动机 线段树合并)

题目链接 \(Description\) 给定串S和m个串Ti.Q次询问,每次询问l,r,pl,pr,求S[pl~pr]在Tl~Tr中的哪个串出现次数最多,输出最多次数及其T的下标.若有多个,输出下标最小的. \(Solution\) 挺好的题吧 对T个串建SAM,然后要求出SAM每个节点上|right|最大的是哪个串. 每个节点的|right|可以在DFS parent树时合并子节点得到,如果用线段树维护,|right|最大的位置也可以合并得到. 这样可以离线处理询问,最后DFS一遍得到答案.

CF666E Forensic Examination(广义后缀自动机+线段树合并)

Luogu 给你一个串 $ S $ 以及一个字符串数组 $ T_1 ~ T_m $ , $ q $ 次询问,每次问 $ S $ 的子串S[p_l,p_r]在 $ T_l ~ T_r $ 中的哪个串里的出现次数最多,并输出出现次数. 如有多解输出最靠前的那一个. 题解时间 SAM的毒瘤题,无论是倍增来满足长度限制,线段树合并来求区间询问,应有尽有... 对于 $ T $ 串建广义SAM,之后考虑如何使得 $ S $ 在SAM上匹配时求出 $ S $ 在每个 $ T $ 的出现次数. 很明显用线段树

【BZOJ3926】[Zjoi2015]诸神眷顾的幻想乡 广义后缀自动机

[BZOJ3926][Zjoi2015]诸神眷顾的幻想乡 Description 幽香是全幻想乡里最受人欢迎的萌妹子,这天,是幽香的2600岁生日,无数幽香的粉丝到了幽香家门前的太阳花田上来为幽香庆祝生日. 粉丝们非常热情,自发组织表演了一系列节目给幽香看.幽香当然也非常高兴啦. 这时幽香发现了一件非常有趣的事情,太阳花田有n块空地.在过去,幽香为了方便,在这n块空地之间修建了n-1条边将它们连通起来.也就是说,这n块空地形成了一个树的结构. 有n个粉丝们来到了太阳花田上.为了表达对幽香生日的祝

Codeforces Round #244 (Div. 2)D (后缀自动机)

Codeforces Round #244 (Div. 2)D (后缀自动机) (标号为0的节点一定是null节点,无论如何都不能拿来用,切记切记,以后不能再错了) 这题用后缀自动机的话,对后缀自动机的很多性质有足够深刻的理解.没想过后缀数组怎么做,因为不高兴敲.... 题意:给出两个长度均不超过5000的字符串s1,s2,求这两个串中,都只出现一次的最短公共子串. 解题思路:求的是公共子串,然后对出现的次数又有限制,第一想法就是后缀自动机啊,后缀自动机处理子串出现次数再合适不过了.做法是这样的

BZOJ 3277 串 (广义后缀自动机)

3277: 串 Time Limit: 10 Sec Memory Limit: 128 MB Submit: 309 Solved: 118 [Submit][Status][Discuss] Description 字符串是oi界常考的问题.现在给定你n个字符串,询问每个字符串有多少子串(不包括空串)是所有n个字符串中至少k个字符串的子串(注意包括本身). Input 第一行两个整数n,k. 接下来n行每行一个字符串. Output 输出一行n个整数,第i个整数表示第i个字符串的答案. Sa

广义后缀自动机

1).自动机的介绍 首先我们先来介绍一下什么是自动机,有限状态自动机的功能是识别字符串,令一个自动机A,若他能识别字符串S,就记为A(S)=Ture,否则A(S)=False. 自动机由五个部分组成,alpha:字符集,state:状态集合,init:初始状态,end:结束状态集合,trans:状态转移函数. 令trans(s,ch)表示当前状态是s,在读入字符ch之后,所到达的状态.如果trans(s,ch)这个转移不存在,为了方便,设其为null,同时null只能转移到null.null表示

hdu 5853 Jong Hyok and String(广义后缀自动机)

题目链接:hdu 5853 Jong Hyok and String 题意: 给你n个字符串,m个询问,每次询问一个字符串 定义set(s)={(i,j)} 表示 s在第i个字符串中出现,且末尾位置为j. 对于一个询问,求set(Qi)=set(t) ,t串的数量. 题解: 如果是n=1,那么就是后缀自动机的一道裸题,答案就是Qi串匹配的最后一个节点x,ml[x]-ml[f[x]]. 现在是多个串,那么就建立一个广义后缀自动机.每次插入一个串后,将last=root,然后继续插下一个就行了. 最