「BZOJ 4502」串
「BZOJ 4502」串
题目描述
兔子们在玩字符串的游戏。首先,它们拿出了一个字符串集合 \(S\),然后它们定义一个字符串为“好”的,当且仅当它可以被分成非空的两段,其中每一段都是字符串集合 \(S\) 中某个字符串的前缀。比如对于字符串集合 \(\{ "abc","bca" \}\),字符串 \("abb"\),\("abab"\)是“好”的 \(("abb"="ab"+"b", abab="ab"+"ab")\) ,而字符串 \(“bc”\)不是“好”的。
兔子们想知道,一共有多少不同的“好”的字符串。
\(1 \leq N \leq 10000, 1 \leq |S| \leq 30\)
解题思路 :
观察发现,对于同一个串可能会有多种划分方式形成两个前缀拼接的形式,直接大力计算不方便处理重复的情况
此时不妨统计每一种答案串中最具有“特征”的那一种划分方式,在所有划分方式中,最小化第二个串的长度
也就是说,如果第一个串已经确定,第二个串的前缀与第一个串的公共部分全部划给第一个串
问题的一部分转化为一个 \(Trie\) 树上求 \(Borders\) 的问题,也就是 \(AC\) 自动机的 \(fail\) 指针,所以可以把问题规约到 \(AC\) 自动机上面
此时答案的形态有两种,拼接起来的串就是原串的一个前缀,或者是两个前缀拼接起来
考虑第一种情况,本质上是对于 \(AC\) 自动机中每一个 \(fail \neq root\) 的点,其到 \(root\) 的路径代表的前缀就是一个合法的答案
对于第二种情况,根据 \(AC\) 自动机的性质,匹配串和合法路径一一对应,所以问题可以转化为对合法路径计数
于是考虑在 \(AC\) 自动机上枚举第一个串,通过 \(dp\) 处理出每一个 \(Trie\) 树节点作为路径终点的答案,通过走树边和 \(fail\) 边来转移
设 \(f[i][j][k]\) 表示总长度 \(i\) 的串走到了节点 \(j\) ,枚举的第一个串的长度为 \(k\) 的答案
转移就直接走 \(Trie\) 图的边转移,但要保证任意时刻拼接起来的串长要能够等于 \(i\) ,也就是 \(dep(j) + k > i\)
但是这样的复杂度是 \(O(n\times60^2\times26)\) 的,时间复杂度不能够接受,考虑对状态进行简化
观察发现,将不等式稍微加变换就是 \(dep(j) > i - k\) ,那么只需要记录 \(f[i][j]\) 表示第二个串长为 \(i\) ,当前到达了节点 \(j\) 的方案数,现在复杂度是 \(O(n \times 26 \times 60)\)
/*program by mangoyang*/
#include<bits/stdc++.h>
#define inf (0x7f7f7f7f)
#define Max(a, b) ((a) > (b) ? (a) : (b))
#define Min(a, b) ((a) < (b) ? (a) : (b))
typedef long long ll;
using namespace std;
template <class T>
inline void read(T &x){
int f = 0, ch = 0; x = 0;
for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1;
for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - '0';
if(f) x = -x;
}
#define int ll
#define par pair<int, int>
#define mp make_pair
#define fi first
#define se second
const int N = 3000005;
char s[N];
int f[65][N], n;
struct ACautomaton{
queue<int> q; int ch[N][26], dep[N], nxt[N][26], fail[N], size;
inline ACautomaton(){
for(int i = 0; i < 26; i++) nxt[0][i] = 1; size = 1;
}
inline int newnode(int x){ return dep[++size] = x, size; }
inline void ins(char *s){
int p = 1, len = strlen(s);
for(int i = 0; i < len; i++){
int c = s[i] - 'a';
if(!ch[p][c]) ch[p][c] = nxt[p][c] = newnode(i + 1);
p = ch[p][c];
}
}
inline void build(){
for(q.push(1); !q.empty(); ){
int u = q.front(); q.pop();
for(int i = 0; i < 26; i++){
int v = nxt[u][i];
if(!v) nxt[u][i] = nxt[fail[u]][i];
else fail[v] = nxt[fail[u]][i], q.push(v);
}
}
}
inline void solve(){
int ans = 0;
for(int i = 2; i <= size; i++) ans += (fail[i] != 1);
for(int i = 1; i <= size; i++)
for(int j = 0; j < 26; j++)
if(!ch[i][j] && nxt[i][j] != 1) f[1][nxt[i][j]]++;
for(int i = 1; i <= 60; i++)
for(int j = 1; j <= size; j++) if(f[i][j]){
for(int c = 0; c < 26; c++)
if(dep[nxt[j][c]] > i) f[i+1][nxt[j][c]] += f[i][j];
}
for(int i = 1; i <= 60; i++)
for(int j = 1; j <= size; j++) ans += f[i][j];
cout << ans;
}
}van;
signed main(){
read(n);
for(int i = 1; i <= n; i++) scanf("%s", s), van.ins(s);
van.build(), van.solve();
return 0;
}
「BZOJ 4502」串的更多相关文章
- 「BZOJ 2534」 L - gap字符串
「BZOJ 2534」 L - gap字符串 题目描述 有一种形如 \(uv u\) 形式的字符串,其中 \(u\) 是非空字符串,且 \(v\) 的长度正好为 \(L\), 那么称这个字符串为 \( ...
- 「BZOJ 4228」Tibbar的后花园
「BZOJ 4228」Tibbar的后花园 Please contact lydsy2012@163.com! 警告 解题思路 可以证明最终的图中所有点的度数都 \(< 3\) ,且不存在环长是 ...
- 「BZOJ 3645」小朋友与二叉树
「BZOJ 3645」小朋友与二叉树 解题思路 令 \(G(x)\) 为关于可选大小集合的生成函数,即 \[ G(x)=\sum[i\in c ] x^i \] 令 \(F(x)\) 第 \(n\) ...
- 「BZOJ 4289」 PA2012 Tax
「BZOJ 4289」 PA2012 Tax 题目描述 给出一个 \(N\) 个点 \(M\) 条边的无向图,经过一个点的代价是进入和离开这个点的两条边的边权的较大值,求从起点 \(1\) 到点 \( ...
- 「BZOJ 2956」模积和
「BZOJ 2956」模积和 令 \(l=\min(n,m)\).这个 \(i\neq j\) 非常不优雅,所以我们考虑分开计算,即: \[\begin{aligned} &\sum_{i=1 ...
- Solution -「BZOJ 3812」主旋律
\(\mathcal{Description}\) Link. 给定含 \(n\) 个点 \(m\) 条边的简单有向图 \(G=(V,E)\),求 \(H=(V,E'\subseteq E)\ ...
- 「BZOJ 1001」狼抓兔子
题目链接 luogu bzoj \(Solution\) 这个貌似没有什么好讲的吧,直接按照这个给的图建图就好了啊,没有什么脑子,但是几点要注意的: 建双向边啊. 要这么写,中间还要写一个\(whil ...
- 「BZOJ 5188」「Usaco2018 Jan」MooTube
题目链接 luogu bzoj \(Describe\) 有一个\(n\)个节点的树,边有权值,定义两个节点之间的距离为两点之间的路径上的最小边权 给你\(Q\)个询问,问你与点\(v\)的距离大于等 ...
- 「BZOJ 2342」「SHOI 2011」双倍回文「Manacher」
题意 记\(s_R\)为\(s\)翻转后的串,求一个串最长的形如\(ss_Rss_R\)的子串长度 题解 这有一个复杂度明显\(O(n)\)的做法,思路来自网上某篇博客 一个双倍回文串肯定当且仅当本身 ...
随机推荐
- HDU 3790 最短生成树 (最短路)
题目链接 Problem Description 给你n个点,m条无向边,每条边都有长度d和花费p,给你起点s终点t,要求输出起点到终点的最短距离及其花费,如果最短距离有多条路线,则输出花费最少的. ...
- struts获得参数(属性,对象,模型驱动)
0. strutsMVC
- java类中访问属性
package first; public class for_protect { private int age=10; int number = 100; public void show(){ ...
- 14 - 函数参数检测-inspect模块
目录 1 python类型注解 2 函数定义的弊端 3 函数文档 4 函数注解 4.1 annotation属性 5 inspect模块 5.1 常用方法 5.2 signature类 5.3 par ...
- Linux 添加普通用户到 sudoers 文件
前言 Linux 的普通用户(uid >= 500)不具有某些命令的执行权限,为了执行较高权限的命令,一般有两种方法: 第一种是使用 su - 命令切换到 root 用户去执行: 另外一种方法是 ...
- Vim的分屏功能
本篇文章主要教你如何使用 Vim 分屏功能. 分屏启动Vim 使用大写的O参数来垂直分屏. vim -On file1 file2 ... 使用小写的o参数来水平分屏. vim -on file1 f ...
- laravel入门教程
参考地址:https://github.com/johnlui/Learn-Laravel-5/issues/16
- P1084 疫情控制
Solution 二分答案, 尽量往上跳, 不能跳到根节点. 仍然能跳的拿出来.看剩下的点没有覆盖哪个? 贪心的分配一下. Code 70 #include<iostream> #incl ...
- Button Bashing(搜索)
aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAAAx8AAAI2CAIAAAC+EqK4AAAgAElEQVR4nOydf0BT9f7/37fS423mWn
- 洛谷P1008三连击 题解
题目传送门 使用dfs搜索,搜索9个数字,注意回溯...最后判断是否符合条件,输出. #include<bits/stdc++.h> using namespace std; ],a[]; ...