Codeforces 235C
2015-06-24 19:10
260 查看
题目大意:
给定一个字符串,接下来再给n个字符串,求原字符串中含有多少个当前给定字符串的循环同构体的字符串的个数
以初始字符串构建后缀自动机,在自动机上前进的时候,比如当前需要匹配的字符串为aba,到达某个状态点S
我们所希望知道的所有aba出现的次数,因为aba最终到达的是点S,其实可以理解为整个后缀自动机通过f(父指针)形成了一棵后缀树
而这个S是后缀树上的叶子节点,那么上方所有的父亲节点都包含这个S,我们只需要找到最顶端的能达到aba状态的根节点,在根状态上记录其下方出现此后缀总共出现的个数
那么首先将后缀自动机拓扑排序,然后节点逆向访问,不断更新父亲节点的信息,用节点上的cnt变量记录当前节点作为根节点时所能得到的后缀的总个数
对于每个当前每个给定的字符串,因为要求循环同构,那么再连接一个相同的字符串在其后方,然后正常在后缀自动机上跑,当匹配到的长度大于len时,说明这个字符串中包含了循环同构体,我们就要不断逆向通过父亲回溯到根节点,这个根节点表示所能达到的长度尽可能小却仍旧大于等于len即可,因为这样的点是当前匹配所有作为大于len的后缀的节点的祖先。然后通过当前节点的flag标记判断当前节点是否被访问过来获取其中的cnt
给定一个字符串,接下来再给n个字符串,求原字符串中含有多少个当前给定字符串的循环同构体的字符串的个数
以初始字符串构建后缀自动机,在自动机上前进的时候,比如当前需要匹配的字符串为aba,到达某个状态点S
我们所希望知道的所有aba出现的次数,因为aba最终到达的是点S,其实可以理解为整个后缀自动机通过f(父指针)形成了一棵后缀树
而这个S是后缀树上的叶子节点,那么上方所有的父亲节点都包含这个S,我们只需要找到最顶端的能达到aba状态的根节点,在根状态上记录其下方出现此后缀总共出现的个数
那么首先将后缀自动机拓扑排序,然后节点逆向访问,不断更新父亲节点的信息,用节点上的cnt变量记录当前节点作为根节点时所能得到的后缀的总个数
对于每个当前每个给定的字符串,因为要求循环同构,那么再连接一个相同的字符串在其后方,然后正常在后缀自动机上跑,当匹配到的长度大于len时,说明这个字符串中包含了循环同构体,我们就要不断逆向通过父亲回溯到根节点,这个根节点表示所能达到的长度尽可能小却仍旧大于等于len即可,因为这样的点是当前匹配所有作为大于len的后缀的节点的祖先。然后通过当前节点的flag标记判断当前节点是否被访问过来获取其中的cnt
#include <cstdio> #include <cstring> #include <iostream> using namespace std; #define N 1000010 #define ll long long struct SamNode{ SamNode *son[26] , *f; int l , cnt , flag; void init(){ for(int i=0 ; i<26 ; i++) son[i] = NULL; f=NULL; l = flag = cnt = 0; } }sam[N<<1] , *root , *last , *b[N<<1]; int cnt , n , num ; char s , str ; void add(int x) { SamNode *p = &sam[++cnt] , *jp = last; p->l = jp->l+1 ; last = p; for( ; jp&&!jp->son[x] ; jp=jp->f) jp->son[x] = p; if(!jp) p->f = root; else{ if(jp->l+1 == jp->son[x]->l) p->f = jp->son[x]; else{ SamNode *r = &sam[++cnt] , *q = jp->son[x]; *r = *q; r->l = jp->l+1; p->f = q->f = r; for( ; jp&&jp->son[x]==q ; jp=jp->f) jp->son[x]=r; } } } void solve() { scanf("%d" , &n); for(int mark=1 ; mark<=n ; mark++){ scanf("%s" , str); int len = strlen(str) , ret = 0 ; ll ans = 0; SamNode *cur = root; for(int i=0 ; i<2*len ; i++){ int x = (i>=len?str[i-len]:str[i])-'a'; if(cur->son[x]){ cur = cur->son[x]; ret++; } else{ while(cur && !cur->son[x]) cur = cur->f; if(!cur) cur=root , ret=0; else{ ret = cur->l+1; cur=cur->son[x]; } } while(cur->f&&cur->f->l>=len){ cur = cur->f; ret = cur->l; } if(ret>=len && cur->flag!=mark) cur->flag=mark , ans= ans+cur->cnt; } printf("%I64d\n" , ans); } } int main() { // freopen("a.in" , "r", stdin); scanf("%s" , s); int len = strlen(s); sam[0].init(); root = last = &sam[cnt=0]; for(int i=0 ; i<len ; i++) add(s[i]-'a'); for(int i=0 ; i<=cnt ; i++) num[sam[i].l]++; for(int i=1 ; i<=len ; i++) num[i] += num[i-1]; for(int i=0 ; i<=cnt ; i++) b[--num[sam[i].l]] = &sam[i]; SamNode *cur = root; for(int i=0 ; i<len ; i++){ cur = cur->son[s[i]-'a']; cur->cnt++; } for(int i=cnt ; i>0 ; i--) b[i]->f->cnt+=b[i]->cnt , b[i]->flag=0; solve(); return 0; }
相关文章推荐
- Python网络编程(3)——SocketServer模块与简单并发服务器
- 二叉树遍历的非递归实现
- Xcode插件失效的解决办法
- [LeetCode] Surrounded Regions
- 自定义安卓的组合控件
- Java中的Object类
- 对IO的初步理解与使用
- 黑马程序员--java 类的继承
- 细谈colorPrimary用于控制ActionBar背景色的好处
- 写给开发者:记录日志的10个建议
- validation插件
- html笔记01:顺序和无序列表
- 后台自动启动appium
- sql中的group by 和 having 用法解析
- Android开发之控件属性
- 设计模式那点事--策略模式
- 解析Cron表达式
- select resharper shortcuts scheme
- Sharepoint学习笔记—Site Definition系列--9、如何在Site Definition中整合Bing Map
- leetcode-12Integer to Roman