后缀自动机感觉好万能

tries图和ac自动机能做的,后缀自动机很多也都可以做

这里的循环匹配则是后缀自动机能做的另一个神奇功能

循环匹配意思就是S是abba, T是abb

问'abb', 'bba','bab'在S中出现过多少次。

我们先把T的末尾循环加一遍,变成abbab

然后把问题转换成,求T的每个后缀和S的最长公共子串

如果最长公共子串的长度大于等于T的长度,那么就说明这个后缀匹配成功

做法就是先对S建立一个后缀自动机,然后记录一个状态

(u, l),u表示当前在后缀自动机匹配的位置,l表示最长公共子串的长度

考虑转移的话,就是

如果下一个位置可以匹配,那么u就到相应的位置,l = l+1

答案更新的时候要注意,如果l大于T的长度len,就需要顺着link往前走到第一个能匹配的位置,即第一个maxlen[x] >= len的地方,然后答案加上endpos[x],不然会丢一部分答案。

如果下一个位置不可以匹配,那么u就顺着link边走,走到第一个能匹配的地方,如果找不到,那u就设成起点,l为0

还有一个问题就是串重复的情况,比如说T是aa,那么扩充就会变成aaa,aa和aa重复。

如果串重复的话,那么必定会到同一个状态,所以一个状态标记一下,只更新一遍答案就可以了

#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
#include <map>
using namespace std;
int n = , len, st;
const int maxL = 1e6 + ;
int maxlen[*maxL], minlen[*maxL], trans[*maxL][], slink[*maxL], lab[*maxL], son[*maxL], endpos[*maxL];
map<int, bool> vis;
int new_state(int _maxlen, int _minlen, int *_trans, int _slink){
maxlen[n] = _maxlen;
minlen[n] = _minlen;
for(int i = ; i < ; i++){
if(_trans == NULL)
trans[n][i] = -;
else
trans[n][i] = _trans[i];
}
slink[n] = _slink;
return n++;
} int add_char(char ch, int u){
int c = ch - 'a';
int z = new_state(maxlen[u]+, -, NULL, -); lab[z] = ;
int v = u;
while(v != - && trans[v][c] == -){
trans[v][c] = z;
v = slink[v];
}
if(v == -){
minlen[z] = ;
slink[z] = ;
return z;
}
int x = trans[v][c];
if(maxlen[v] + == maxlen[x]){
minlen[z] = maxlen[x] + ;
slink[z] = x;
return z;
}
int y = new_state(maxlen[v] + , -, trans[x], slink[x]);
slink[y] = slink[x];
minlen[x] = maxlen[y] + ;
slink[x] = y;
minlen[z] = maxlen[y] + ;
slink[z] = y;
int w = v;
while(w != - && trans[w][c] == x){
trans[w][c] = y;
w = slink[w];
}
minlen[y] = maxlen[slink[y]] + ;
return z;
} char str[maxL];
int main()
{
cin>>str;
st = new_state(, , NULL, -);
int len = strlen(str);
for(int i = ; i < len; i++) {
st = add_char(str[i], st);
}
for(int i = ; i <= n; i++) son[slink[i]]++;
queue<int> Q;
for(int i = ; i <= n; i++) if(son[i] == ) Q.push(i), endpos[i] = ;
while(!Q.empty()){
int x = Q.front(); Q.pop();
if(x == ) continue;
int y = slink[x];
son[y]--; endpos[y] += endpos[x];
if(son[y] == ){
if(lab[y]) endpos[y]++;
Q.push(y);
}
}
int T;
cin>>T;
while(T--){
vis.clear();
cin>>str;
int len = strlen(str), ylen = len;
for(int i = len; i < *len-; i++) str[i] = str[i-len];
len = *len-;
int u = , l = , ans = ;
for(int i = ; i < len; i++){
int c = str[i] - 'a';
if(trans[u][c] != -){
u = trans[u][c];
l++;
} else {
int y = slink[u];
while(y != -){
if(trans[y][c] != -){
l = maxlen[y] + ;
u = trans[y][c];
break;
}
u = y;
y = slink[u];
}
if(y == -) { u = ; l = ; }
}
if(l >= ylen){
int y = slink[u];
while(maxlen[y] >= ylen) { u = y; y = slink[u]; l = maxlen[u]; }
if(vis[u]) continue;
vis[u] = ;
ans += endpos[u];
}
}
cout<<ans<<endl;
}
return ;
}

hihocoder 1465 循环串匹配问题(后缀自动机)的更多相关文章

  1. hiho# 1465 重复旋律8 循环串计数 后缀自动机

    题目传送门 题意:给出一个母串,再给出n个串,问对于每个串,母串中有几个子串是可以通过循环变化得到这个串. 思路:对母串建SAM,求出$right$集. 把匹配串复制一遍,和母串进行匹配,当匹配长度大 ...

  2. hihoCoder #1465 : 后缀自动机五·重复旋律8

    http://hihocoder.com/problemset/problem/1465 求S的循环同构串在T中的出现次数 将串S变成SS 枚举SS的每个位置i,求出以i结尾的SS的子串 与 T的最长 ...

  3. hihocoder 后缀自动机五·重复旋律8 求循环同构串出现的次数

    描述 小Hi平时的一大兴趣爱好就是演奏钢琴.我们知道一段音乐旋律可以被表示为一段数构成的数列. 小Hi发现旋律可以循环,每次把一段旋律里面最前面一个音换到最后面就成为了原旋律的“循环相似旋律”,还可以 ...

  4. 后缀自动机专题(hihocoder)

    传送门 #1445 : 后缀自动机二·重复旋律5 题意: 给出字符串\(s\),询问字符串\(s\)中有多少不同的子串. 思路: 考虑对\(s\)建后缀自动机,那么\(\sum (len[i]-len ...

  5. 【hihocoder#1413】Rikka with String 后缀自动机 + 差分

    搞了一上午+接近一下午这个题,然后被屠了个稀烂,默默仰慕一晚上学会SAM的以及半天4道SAM的hxy大爷. 题目链接:http://hihocoder.com/problemset/problem/1 ...

  6. hihoCoder 后缀自动机三·重复旋律6

    后缀自动机三·重复旋律6 时间限制:15000ms 单点时限:3000ms 内存限制:512MB 描述 小Hi平时的一大兴趣爱好就是演奏钢琴.我们知道一个音乐旋律被表示为一段数构成的数列. 现在小Hi ...

  7. 【hihoCoder 1466】后缀自动机六·重复旋律9

    http://hihocoder.com/problemset/problem/1466 建出A串和B串的两个后缀自动机 对后缀自动机的每个状态求出sg值. 求出B串的\(sum(x)\),表示B有多 ...

  8. hihocoder 后缀自动机专题

    一.后缀自动机基本概念的理解 1.首先后缀自动机的状态是由子串的endpos来决定的 子串的endpos是指一个子串可以在原字符串的哪些位置进行匹配, endpos构成的不同集合划分成不同的状态 关于 ...

  9. hiho一下第131周 后缀自动机二·重复旋律8(循环相似子串)

    后缀自动机五·重复旋律8 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 小Hi平时的一大兴趣爱好就是演奏钢琴.我们知道一段音乐旋律可以被表示为一段数构成的数列. 小Hi ...

随机推荐

  1. LVS、keepalived原理及配置

    使用LVS实现负载均衡原理及安装配置详解 ​ 负载均衡集群是 load balance 集群的简写,翻译成中文就是负载均衡集群.常用的负载均衡开源软件有nginx.lvs.haproxy,商业的硬件负 ...

  2. JQuery中使用FormData异步提交数据和提交文件

    web中数据提交事件是常常发生的,但是大多数情况下我们不希望使用html中的form表单提交,因为form表单提交会中断当前浏览器的操作并且会调到另一个地址(即使这个地址是当前页面),并且会重复加载一 ...

  3. 在ReactNative中使用Typescript

    在ReactNative中使用Typescript 少侠放心,跟着我的这个步骤走,保你完美在RN项目中使用Typescript,废话不多说,走你 1.全局安装create-react-native-a ...

  4. C#在textBox中输出一个数组

    //将数组输出到文本框测试 for(i=0;i<arr.Length-1;i++){ this.textBox1.Text=this.textBox1.Text+arr[i]; }

  5. php导出excel长数字串显示为科学计数方法与最终解决方法

    1.设置单元格为文本 $objPHPExcel = new PHPExcel(); $objPHPExcel->setActiveSheetIndex(0); $objPHPExcel-> ...

  6. scala成长之路(7)函数进阶——可能是史上最浅显易懂的闭包教程

    由于scala中函数内部能定义函数,且函数能作为函数的返回值,那么问题来了,当返回的函数使用了外层函数的局部变量时,会发生什么呢?没错,就产生是闭包. 关于闭包的解释网上一大堆,但基本上都是照葫芦画瓢 ...

  7. Hive初识(一)

    LOAD DATA语句 一般来说,在SQL创建表后,我们就可以使用INSERT语句插入数据.但在Hive中,可以使用LOAD DATA语句来插入数据. LOAD DATA [LOCAL] INPATH ...

  8. C# 隐藏窗口标题栏、隐藏任务栏图标

    //没有标题 this.FormBorderStyle = FormBorderStyle.None; //任务栏不显示 this.ShowInTaskbar = false;

  9. fopen,fwrite,fread使用

    fopen, fwrite, fread详解 1.头文件 #include <stdio.h> 2.fopen (1) 函数原型 FILE *fopen(char *filename, * ...

  10. 2.PostgreSQL安装详细步骤(windows)【转】

    感谢 Junn9527 PostgreSQL安装:一.windows下安装过程安装介质:postgresql-9.1.3-1-windows.exe(46M),安装过程非常简单,过程如下:1.开始安装 ...