Boring counting HDU - 3518 (后缀自动机)
Boring counting
\]
题意
给出一个字符串,求出其中出现两次及以上的子串个数,要求子串之间不可以重合。
思路
在 \(SAM\) 上对于节点 \(i\) ,其包含的子串长度范围为 \(\left[maxlen\left(father\right)+1,maxlen\left(i\right) \right]\),在考虑节点\(i\)的 \(endpos\),设出现的最左位置为 \(left\),最右位置为 \(right\),如果我们可以得到 \(left\) 和 \(right\),我们就可以进行如下的讨论:
- 首先明确 \(right-left\) 是同一子串两次出现位置最大距离,我们考虑在这个距离中,可以不重合的放下多少子串。
若 \(right-left \geq maxlen(i)\),距离足够大放下节点 \(i\) 可以表示的最长子串。这说明对于节点 \(i\) 包含的所有子串,都能满足不重合的出现两次。一共有 \(maxlen(i)-maxlen(father)\) 种子串符合条件。
若 \(right-left \leq maxlen(father)\),此时距离太小,无法放下节点 \(i\) 可以表示的最短子串。这说明对于节点 \(i\) 包含的所有子串,都无法满足不重合的出现两次。
若 \(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 (后缀自动机)的更多相关文章
- Boring counting HDU - 3518 后缀自动机
题意: 对于给出的字符串S, 长度不超过1000, 求其中本质不同的子串的数量, 这些子串满足在字符串S中出现了至少不重合的2次 题解: 将串放入后缀自动机中然后求出每一个节点对应的子串为后缀的子串出 ...
- Boring counting HDU - 3518 (后缀数组)
Boring counting \[ Time Limit: 1000 ms \quad Memory Limit: 32768 kB \] 题意 给出一个字符串,求出其中出现两次及以上的子串个数,要 ...
- POJ 3518 (后缀自动机)
POJ 3518 Boring Problem : 给一个串S,询问串S有多个子串出现至少两次且位置不重叠. Solution : 对S串建立后缀自动机,再建立后缀树,dfs一遍统计处每个结点的子树中 ...
- HDU 5442 后缀自动机(从环字符串选定一个位置 , 时针或顺时针走一遍,希望得到字典序最大)
http://acm.hdu.edu.cn/showproblem.php?pid=5442 题目大意: 给定一个字符串,可理解成环,然后选定一位置,逆时针或顺时针走一遍,希望得到字典序最大,如果同样 ...
- HDU 4436 (后缀自动机)
HDU 4436 str2int Problem : 给若干个数字串,询问这些串的所有本质不同的子串转换成数字之后的和. Solution : 首先将所有串丢进一个后缀自动机.由于这道题询问的是不同的 ...
- HDU 4622 (后缀自动机)
HDU 4622 Reincarnation Problem : 给一个串S(n <= 2000), 有Q个询问(q <= 10000),每次询问一个区间内本质不同的串的个数. Solut ...
- HDU 4416 (后缀自动机)
HDU 4416 Good Article Good sentence Problem : 给一个串S,和一些串T,询问S中有多少个子串没有在T中出现. Solution :首先对所有的T串建立后缀自 ...
- HDU 5442 后缀自动机+kmp
题目大意: 给定一个字符串,可理解成环,然后选定一位置,逆时针或顺时针走一遍,希望得到字典序最大,如果同样大,希望找到起始位置最小的,如果还相同,就默认顺时针 比赛一直因为处理最小位置出错,一结束就想 ...
- hdu 6208(后缀自动机、或者AC自动机
题意:给你n个字符串,问你是否存在一个字符串可以从中找到其他n-1个字符串. 思路:其实很简单,找到最长的那个字符串对他进行匹配,看是否能匹配到n-1个字符串. 可以用AC自动机或者后缀自动机做,但是 ...
随机推荐
- pycharm社区办找不到View-->Tool Windows->DataBase的解决办法
File->Settings->Plugins->Database Navigator 安装
- js实现对上传图片的路径转成base64编码,并且对图片进行压缩,实现预览功能1
参考 https://blog.csdn.net/qq_31965515/article/details/82975381 https://www.cnblogs.com/zhangdiIT/p/78 ...
- Linux crond任务调度(定时任务),Linux磁盘分区/挂载
一.crond任务调度 1.基本语法 crontab [选项] -e : 编辑 crontab定时任务 -l : 查询crontab -r : 删除当前用户所有的crontab任务 例子: 每分钟执行 ...
- C语言输入带空格的字符串
参考:https://blog.csdn.net/vincemar/article/details/78750435 因为: scanf("%s",str); 遇到空格就停止接收后 ...
- 百度前端技术学院task14源代码
刚开始理解错误,以为是读取对象,结果后面才发现是二维数组. 另外对于数组排序,创建新的节点啊,输入到doM中啊,都不是很熟悉. <!DOCTYPE html> <html> & ...
- JS异步操作概述(转)
add by zhj: 只转载了一部分.异步操作的三种模式未转载,因为里面代码比较多,复制过来麻烦 原文:https://wangdoc.com/javascript/async/general.ht ...
- AGC035
Contest Page A 唯一会做的题/kk 题目相当于要求相邻三个的异或和为\(0\). 当我们放入了三个数\(a,b,c\)时,接下来的放入顺序显然一定是\(a,b,c,a,b,c,...\) ...
- 使用el-dialog时,报错“Unknown custom element:<el-dialog> did you register the component correctly?...make sure to provide the 'name' option”
初学vue时,曾遇到一个无语的问题,在用el-dialog时一直显示没有导入,结果发现是因为没有把element ui引入到项目里. 需进行以下步骤: 1.执行: npm install elemen ...
- ML学习笔记之Anaconda中命令形式安装XGBoost(pip install)
0x00 概述 在没有安装XGBoost之前,import xgboot会出错,如下: # ModuleNotFoundError: No module named ‘xgboost’ 0x01 安装 ...
- 一文搞懂 Flink 网络流控与反压机制
https://www.jianshu.com/p/2779e73abcb8 看完本文,你能get到以下知识 Flink 流处理为什么需要网络流控? Flink V1.5 版之前网络流控介绍 Flin ...