Boring counting

\[Time Limit: 1000 ms \quad Memory Limit: 32768 kB
\]

题意

给出一个字符串,求出其中出现两次及以上的子串个数,要求子串之间不可以重合。

思路

在 \(SAM\) 上对于节点 \(i\) ,其包含的子串长度范围为 \(\left[maxlen\left(father\right)+1,maxlen\left(i\right) \right]\),在考虑节点\(i\)的 \(endpos\),设出现的最左位置为 \(left\),最右位置为 \(right\),如果我们可以得到 \(left\) 和 \(right\),我们就可以进行如下的讨论:

  • 首先明确 \(right-left\) 是同一子串两次出现位置最大距离,我们考虑在这个距离中,可以不重合的放下多少子串。
  1. 若 \(right-left \geq maxlen(i)\),距离足够大放下节点 \(i\) 可以表示的最长子串。这说明对于节点 \(i\) 包含的所有子串,都能满足不重合的出现两次。一共有 \(maxlen(i)-maxlen(father)\) 种子串符合条件。

  2. 若 \(right-left \leq maxlen(father)\),此时距离太小,无法放下节点 \(i\) 可以表示的最短子串。这说明对于节点 \(i\) 包含的所有子串,都无法满足不重合的出现两次。

  3. 若 \(maxlen(father) < right-left < maxlen(i)\), 此时距离只够放下其中的一部分子串。这时候容易得到,可以放下的子串的长度范围为 \(\left[maxlen(father)+1,right-left\right]\),也就是有 \(right-left-maxlen(father)\) 种子串符合条件。

综合上面三种情况,整理起来就是

  • 若 \(right-left \leq maxlen(father)\),对答案贡献0。
  • 否则,对答案贡献 \(min(right-left,maxlen(i)) - maxlen(father)\)。

那么如何得到 \(left\)和\(right\)?

  • 对于 \(left\),每个子串第一次出现的位置,一定就是他的 \(left\)。
  • 对于 \(right\),因为 \(endpos(i) \in endpos(father)\),所以将每个节点的 \(left\) 往其 \(father\) 上更新最大值,就是 \(father\) 的 \(right\),如此倒着求 \(right\)。
/***************************************************************
> File Name : a.cpp
> Author : Jiaaaaaaaqi
> Created Time : 2019年05月23日 星期四 00时06分46秒
***************************************************************/ #include <map>
#include <set>
#include <list>
#include <ctime>
#include <cmath>
#include <stack>
#include <queue>
#include <cfloat>
#include <string>
#include <vector>
#include <cstdio>
#include <bitset>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define lowbit(x) x & (-x)
#define mes(a, b) memset(a, b, sizeof a)
#define fi first
#define se second
#define pii pair<int, int> typedef unsigned long long int ull;
typedef long long int ll;
const int maxn = 1e3 + 10;
const int maxm = 1e5 + 10;
const ll mod = 1e9 + 7;
const ll INF = 1e18 + 100;
const int inf = 0x3f3f3f3f;
const double pi = acos(-1.0);
const double eps = 1e-8;
using namespace std; int n, m;
int cas, tol, T; struct SAM {
struct Node{
int next[27];
int fa, len;
int left, right;
void init() {
mes(next, 0);
fa = len = left = right = 0;
}
} node[maxn<<1];
vector<int> vv[maxn<<1];
int sz, last;
void init() {
sz = last = 1;
node[sz].init();
}
void insert(int k, int id) {
int p = last, np = last = ++sz;
node[np].init();
node[np].len = node[p].len + 1;
node[np].left = node[np].right = id;
for(; p&&!node[p].next[k]; p=node[p].fa)
node[p].next[k] = np;
if(p == 0) {
node[np].fa = 1;
} else {
int q = node[p].next[k];
if(node[q].len == node[p].len + 1) {
node[np].fa = q;
} else {
int nq = ++sz;
node[nq] = node[q];
node[nq].len = node[p].len+1;
node[nq].left = node[q].left;
node[nq].right = node[q].right;
node[np].fa = node[q].fa = nq;
for(; p&&node[p].next[k]==q; p=node[p].fa)
node[p].next[k] = nq;
}
}
}
bool vis[maxn<<1];
void dfs(int u) {
if(vis[u]) return ;
vis[u] = true;
for(auto v : vv[u]) {
dfs(v);
node[u].right = max(node[u].right, node[v].right);
}
}
void build() {
for(int i=1; i<=sz; i++) vv[i].clear();
for(int i=2; i<=sz; i++) {
vv[node[i].fa].push_back(i);
}
mes(vis, 0);
dfs(1);
}
int finalans;
void DFS(int u) {
if(vis[u]) return ;
vis[u] = true;
for(auto v : vv[u]) {
DFS(v);
int l = node[v].left, r = node[v].right;
if(r-l > node[u].len) {
finalans += min(node[v].len, r-l) - node[u].len;
}
}
return ;
}
int solve() {
finalans = 0;
mes(vis, 0);
DFS(1);
return finalans;
}
} sam;
char s[maxn]; int main() {
while(scanf("%s", s+1)) {
if(s[1] == '#') break;
sam.init();
int len = strlen(s+1);
for(int i=1; i<=len; i++) {
sam.insert(s[i]-'a'+1, i);
}
sam.build();
// for(int i=1; i<=sam.sz; i++) {
// printf("%d left = %d right = %d\n", i, sam.node[i].left, sam.node[i].right);
// }
printf("%d\n", sam.solve());
}
return 0;
}

Boring counting HDU - 3518 (后缀自动机)的更多相关文章

  1. Boring counting HDU - 3518 后缀自动机

    题意: 对于给出的字符串S, 长度不超过1000, 求其中本质不同的子串的数量, 这些子串满足在字符串S中出现了至少不重合的2次 题解: 将串放入后缀自动机中然后求出每一个节点对应的子串为后缀的子串出 ...

  2. Boring counting HDU - 3518 (后缀数组)

    Boring counting \[ Time Limit: 1000 ms \quad Memory Limit: 32768 kB \] 题意 给出一个字符串,求出其中出现两次及以上的子串个数,要 ...

  3. POJ 3518 (后缀自动机)

    POJ 3518 Boring Problem : 给一个串S,询问串S有多个子串出现至少两次且位置不重叠. Solution : 对S串建立后缀自动机,再建立后缀树,dfs一遍统计处每个结点的子树中 ...

  4. HDU 5442 后缀自动机(从环字符串选定一个位置 , 时针或顺时针走一遍,希望得到字典序最大)

    http://acm.hdu.edu.cn/showproblem.php?pid=5442 题目大意: 给定一个字符串,可理解成环,然后选定一位置,逆时针或顺时针走一遍,希望得到字典序最大,如果同样 ...

  5. HDU 4436 (后缀自动机)

    HDU 4436 str2int Problem : 给若干个数字串,询问这些串的所有本质不同的子串转换成数字之后的和. Solution : 首先将所有串丢进一个后缀自动机.由于这道题询问的是不同的 ...

  6. HDU 4622 (后缀自动机)

    HDU 4622 Reincarnation Problem : 给一个串S(n <= 2000), 有Q个询问(q <= 10000),每次询问一个区间内本质不同的串的个数. Solut ...

  7. HDU 4416 (后缀自动机)

    HDU 4416 Good Article Good sentence Problem : 给一个串S,和一些串T,询问S中有多少个子串没有在T中出现. Solution :首先对所有的T串建立后缀自 ...

  8. HDU 5442 后缀自动机+kmp

    题目大意: 给定一个字符串,可理解成环,然后选定一位置,逆时针或顺时针走一遍,希望得到字典序最大,如果同样大,希望找到起始位置最小的,如果还相同,就默认顺时针 比赛一直因为处理最小位置出错,一结束就想 ...

  9. hdu 6208(后缀自动机、或者AC自动机

    题意:给你n个字符串,问你是否存在一个字符串可以从中找到其他n-1个字符串. 思路:其实很简单,找到最长的那个字符串对他进行匹配,看是否能匹配到n-1个字符串. 可以用AC自动机或者后缀自动机做,但是 ...

随机推荐

  1. gitblit搭建

    gitblit 1.8.0  + java7 下载安装java7 https://download.oracle.com/otn/java/jdk/7u80-b15/jdk-7u80-windows- ...

  2. mvc_1_ex_stu_manage

    Mvc第一遍结束综合练习:带权限的学生管理系统程序的设计应该根据功能来进行.先来设想一下本练习的程序功能:学生信息管理.登录用户区分权限.出错应该给出提示.由此可以设想,完成以后的程序是下图的样子:主 ...

  3. Centos7安装Tomcat7,并上传JavaWeb项目

    一.需要的工具(其他连接工具也行) 1.Xshell 2.XFTP 1.1首先将Tomcat7的压缩文件利用XFTP上传到Centos7系统上的 /etc/local/tomcat中 1.2 解压文件 ...

  4. kylin安装过程问题排查

    问题:日志报错:/usr/local/apps/kylin/tomcat/conf/.keystore (没有那个文件或目录) 解决:在kylin内置tomcat的server.xml中里边有个对ht ...

  5. [摘抄] 3.AMD规范与CommonJS规范的兼容性

    3. AMD规范与CommonJS规范的兼容性 CommonJS规范加载模块是同步的,也就是说,只有加载完成,才能执行后面的操作. AMD规范则是非同步加载模块,允许指定回调函数. 由于Node.js ...

  6. 另类WebShell监测机制--基于auditd

    鸣  谢 VSRC感谢业界小伙伴——老陈投稿精品原创类文章.VSRC欢迎精品原创类文章投稿,优秀文章一旦采纳发布,将有好礼相送,我们为您准备的丰富奖品包括但不仅限于:MacbookAir.VSRC定制 ...

  7. Process.Start可能无法选中指定文件的问题

    简单的说是由于给定的文件路径中含有多余的斜杠(\),比如C:\a\b\\c.txt,在.NET类(比如File,FileInfo,Directory)中使用没有问题,但是如果使用Process.Sta ...

  8. jQuery遍历之find()

    /**案例说明: *首先理清楚find()函数同children()函数之间的区别 * 1. find()会遍历给定节点下的所有的元素节点. * 2. children()之后遍历给定节点下的单一层级 ...

  9. tensorflow批量读取数据

    Tensorflow 数据读取有三种方式: Preloaded data: 预加载数据,在TensorFlow图中定义常量或变量来保存所有数据(仅适用于数据量比较小的情况). Feeding: Pyt ...

  10. error: stdio.h: 没有那个文件或目录

    在64位系统中,编写一个C语言程序后,使用gcc进行编译时,出现了如下的错误: test.c:1:19: fatal  error: stdio.h: 没有那个文件或目录 #include <s ...