http://blog.csdn.net/gatevin/article/details/45875343

题目是求不重叠的不同子串个数

一般来说,

endpos集合包含了子串结尾位置,结尾在"3、4、6"等

每个状态都包含了若干个连续子串。就是"aabab", "abbab", "bbab", "bab"属于同一个状态

endpos集合的大小就是这些子串的出现次数

但是这样会重叠。那么可以求出endpos结合的结尾最小值,和结尾最大值。

那么长度小于mx - mi的子串,是肯定不会重叠的

至于有多少个,可以由mxcnt决定

#include <bits/stdc++.h>
#define IOS ios::sync_with_stdio(false)
using namespace std;
#define inf (0x3f3f3f3f)
typedef long long int LL;
const int maxn = + , N = ;
struct Node {
int mxCnt; //mxCnt表示后缀自动机中当前节点识别子串的最大长度
int miCnt; //miCnt表示后缀自动机中当前节点识别子串的最小长度
int id; //表示它是第几个后缀自动机节点,指向了它,但是不知道是第几个,用id判断
int mxPos, miPos; //pos表示它在原串中的位置。
bool flag; //表示当前节点是否能识别前缀
struct Node *pNext[N], *fa;
}suffixAutomaton[maxn * ], *root, *last; //大小需要开2倍,因为有一些虚拟节点
int t; //用到第几个节点
struct Node *create(int mxCnt = -, struct Node *node = NULL) { //新的节点
if (mxCnt != -) {
suffixAutomaton[t].mxCnt = mxCnt, suffixAutomaton[t].fa = NULL;
for (int i = ; i < N; ++i) suffixAutomaton[t].pNext[i] = NULL;
} else {
suffixAutomaton[t] = *node; //保留了node节点所有的指向信息。★全部等于node
//可能需要注意下pos,在原串中的位置。现在pos等于原来node的pos
}
suffixAutomaton[t].id = t; //必须要有的,不然id错误
suffixAutomaton[t].flag = false; //默认不是前缀节点
return &suffixAutomaton[t++];
}
void addChar(int x, int pos) { //pos表示在原串的位置
struct Node *p = last, *np = create(p->mxCnt + , NULL);
np->flag = true;
np->mxPos = np->miPos = pos, last = np; //last是最尾那个可接收后缀字符的点。
for (; p != NULL && p->pNext[x] == NULL; p = p->fa) p->pNext[x] = np;
if (p == NULL) {
np->fa = root;
np->miCnt = ; // 从根节点引一条边过来
return;
}
struct Node *q = p->pNext[x];
if (q->mxCnt == p->mxCnt + ) { //中间没有任何字符,可以用来代替接受后缀、
np->fa = q;
np->miCnt = q->mxCnt + ; // q是状态8的"ab",np是状态7的"bab"长度是2+1
return;
}
// p: 当前往上爬到的可以接受后缀的节点
// np:当前插入字符x的新节点
// q: q = p->pNext[x],q就是p中指向的x字符的节点
// nq:因为q->cnt != p->cnt + 1而新建出来的模拟q的节点
struct Node *nq = create(-, q); // 新的q节点,用来代替q,帮助np接收后缀字符
nq->mxCnt = p->mxCnt + ; //就是需要这样,这样中间不包含任何字符
q->miCnt = nq->mxCnt + , np->miCnt = nq->mxCnt + ;
q->fa = nq, np->fa = nq; //现在nq是包含了本来q的所有指向信息
for (; p && p->pNext[x] == q; p = p->fa) {
p->pNext[x] = nq;
}
}
void init() {
t = ;
root = last = create(, NULL);
}
void build(char str[], int lenstr) {
init();
for (int i = ; i <= lenstr; ++i) addChar(str[i] - 'a', i);
}
char str[maxn];
queue<int> que;
int dp[maxn * ], in[maxn * ];
void work() {
scanf("%s", str + );
build(str, strlen(str + ));
for (int i = ; i < t; ++i) {
in[suffixAutomaton[i].fa->id]++;
}
for (int i = ; i < t; ++i) {
if (in[i] == ) que.push(i);
}
while (!que.empty()) {
int cur = que.front();
que.pop();
if (!cur) break;
int fa = suffixAutomaton[cur].fa->id;
suffixAutomaton[fa].mxPos = max(suffixAutomaton[fa].mxPos, suffixAutomaton[cur].mxPos);
in[fa]--;
if (in[fa] == ) que.push(fa);
}
LL ans = ;
for (int i = ; i < t; ++i) {
int dis = suffixAutomaton[i].mxPos - suffixAutomaton[i].miPos;
int mi = min(suffixAutomaton[i].mxCnt, dis);
if (dis < suffixAutomaton[i].miCnt) continue;
ans += mi - suffixAutomaton[i].miCnt + ;
}
printf("%lld\n", ans);
} int main() {
#ifdef local
freopen("data.txt", "r", stdin);
// freopen("data.txt", "w", stdout);
#endif
work();
return ;
}

SETI ACdream - 1430 后缀自动机求不相交子串的更多相关文章

  1. str2int HDU - 4436 后缀自动机求子串信息

    题意: 给出 n 个串,求出这 n 个串所有子串代表的数字的和. 题解; 首先可以把这些串构建后缀自动机(sam.last=1就好了), 因为后缀自动机上从 root走到的任意节点都是一个子串,所有可 ...

  2. BZOJ 3998: [TJOI2015]弦论 后缀自动机 后缀自动机求第k小子串

    http://www.lydsy.com/JudgeOnline/problem.php?id=3998 后缀自动机应用的一个模板?需要对len进行一个排序之后再统计每个出现的数量,维护的是以该字符串 ...

  3. 洛谷P4248 [AHOI2013]差异(后缀自动机求lcp之和)

    题目见此 题解:首先所有后缀都在最后一个np节点,然后他们都是从1号点出发沿一些字符边到达这个点的,所以下文称1号点为根节点,我们思考一下什么时候会产生lcp,显然是当他们从根节点开始一直跳相同节点的 ...

  4. Alice's Classified Message HDU - 5558 后缀自动机求某个后缀出现的最早位置

    题意: 给定一个长度不超过 10W 的只包含小写字母的字符串,从下标 0 到 n−1.从下标 0 开始操作, 每次对于下标 pos查找下标 pos 开始的子串中最长的在其他地方出现过的长度,其他出现的 ...

  5. 后缀自动机求字典序第k小的串——p3975

    又领悟到了一点新的东西,后缀自动机其实可以分为两个数据结构,一个是后缀树,还有一个是自动机 后缀树用来划分endpos集合,并且维护后缀之间的关系,此时每个结点代表的是一些后缀相同且长度连续的子串 自 ...

  6. JDOJ 2939: Suffix Automaton 广义后缀自动机_统计子串

    建立广义后缀自动机,对每个节点都建立各自的 $Parent$ 数组. 这样方便统计,不会出现统计错误. 考虑新加入一个字符. 1 这条转移边已经存在,显然对答案没有贡献. 2 这条转移边不存在,贡献即 ...

  7. CodeForces-204E:Little Elephant and Strings (广义后缀自动机求出现次数)

    The Little Elephant loves strings very much. He has an array a from n strings, consisting of lowerca ...

  8. 洛谷 P1368 工艺 后缀自动机 求最小表示

    后缀自动机沙茶题 将字符串复制一次,建立后缀自动机. 在后缀自动机上贪心走 $n$ 次即可. Code: #include <cstdio> #include <algorithm& ...

  9. 后缀自动机模板——不同子串个数p2408

    后缀自动机的入门博客 https://www.luogu.org/blog/Kesdiael3/hou-zhui-zi-dong-ji-yang-xie 有两种求法,分别对应了两种性质 #includ ...

随机推荐

  1. Servlet入门第二天

    1. GenericServlet: 1). 是一个 Serlvet. 是 Servlet 接口和 ServletConfig 接口的实现类. 但是一个抽象类. 其中的 service 方法为抽象方法 ...

  2. 《Effective Java》第3章 对于所有对象都通用的方法

    第8条:覆盖equals时请遵守通用约定 覆盖equals方法看起来似乎很简单,但是有许多覆盖方式会导致错误,并且后果非常严重.最容易避免这类问题的办法就是不覆盖equals方法,在这种情况下,类的每 ...

  3. JaVA web服务器配置

    1:第一是下载好Eclipse开发工具,这里不做叙述,自行下载安装. 2:使用Eclipse开发WEB项目,启动Eclipse,选择File--->new --->other---> ...

  4. Unobrusive Ajax使用

    mark一下:[ASP.NET MVC 小牛之路]14 - Unobtrusive Ajax篇文章,果断记下来,网址: http://www.cnblogs.com/willick/p/3418517 ...

  5. asp.net core tags 扩展之 id 和 name

    asp.net core 页面 TagHelper  的 Id 和 Name 属性扩展 . [HtmlTargetElement(Attributes = "asp-name")] ...

  6. 获取GridView控件总列数

    GridView控件,它不管是放在MasterPage母版页内,还是放在Page单独网页内,它不管是自动显示列AutoGenerateColumns="true",还是手动定列Au ...

  7. 正经学C#_判断[IF语句]:[c#入门经典]

    判断语句几乎是最为常用的语句之一,是最有效的. 先说IF语句,IF语句也是属于分支的一种,用来控制流程的. IF的语句是这样的 IF(xxx 条件) { //代码块 } ,b; ) { b=a--; ...

  8. python之编辑器pycharm

    在进行python开发的时候,习惯使用pycharm这个编辑器进行开发工作,总结一些常用到的功能点 常用功能: 1. 打开当前文件所在的目录 在文件右击 -> Show in Exploer   ...

  9. ubuntu - 官方简体中文wiki,有你想要的ubuntu的一切

    ubuntu官方简体中文wiki:http://wiki.ubuntu.org.cn/%E9%A6%96%E9%A1%B5

  10. SQLException: Incorrect string value: '\xE4\xB8\xAD\xE5\x9B\xBD' for column at row 1

    这个是由于新建数据库没有选择默认字符集导致的,只要选择utf8即可. 如果以上还无法解决,那可能是表里的varchar字符集也不对