「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)\)的做法,思路来自网上某篇博客 一个双倍回文串肯定当且仅当本身 ...
随机推荐
- windows下gitlab配置 生成ssh key
Git-1.9.5-preview20141217 1. 安装git,从程序目录打开 "Git Bash" 2. 键入命令:ssh-keygen -t rsa -C "e ...
- 写一个简易浏览器、ASP.NET核心知识(3)
前言 先在文章前面说好了,省得大家发现我根本没有这样的头发,duang的一下一堆人骂我. 这篇文章的标题有点大,其实挺low的,我需要在开头解释一下.我这里只想写一个小的控制台,旨在模拟浏览器的htt ...
- UIWebView---iOS-Apple苹果官方文档翻译
CHENYILONG Blog UIWebView---iOS-Apple苹果官方文档翻译 UIWebView 技术博客http://www.cnblogs.com/ChenYilong/ 新浪微博h ...
- java对象与json互转
package com.liveyc; import java.io.StringWriter; import org.junit.Test; import com.fasterxml.jackson ...
- A .Gaby And Addition (Gym - 101466A + 字典树)
题目链接:http://codeforces.com/gym/101466/problem/A 题目: 题意: 给你n个数,重定义两个数之间的加法不进位,求这些数中两个数相加的最大值和最小值. 思路: ...
- 2017ACM暑期多校联合训练 - Team 3 1003 HDU 6058 Kanade's sum (模拟)
题目链接 Problem Description Give you an array A[1..n]of length n. Let f(l,r,k) be the k-th largest elem ...
- python 输出 a+b
AC代码: 单组输入: s=input().split() print(int(s[0])+int(s[1]))
- python进阶之关键字和运算符触发魔法方法
前言 python有众多的魔法方法,它们会在满足某种条件下触发执行,掌握好魔法方法的使用,可以加快程序的运行效率,同时减少逻辑调用. 关键字与魔法方法 python的一些魔法方法是关键字触发的,即py ...
- nginx之日志设置详解
nginx的日志设置 access_log access_log是服务器记录了哪些用户,哪些页面以及用户浏览器.ip和其他的访问信息:是一种非常详细的记录信息:如果我们不关心谁访问了我们,可以关闭: ...
- 15个你不得不知道的Chrome dev tools的小技巧
转载自:https://www.imooc.com/article/2559 谷歌浏览器如今是Web开发者们所使用的最流行的网页浏览器.伴随每六个星期一次的发布周期和不断扩大的强大的开发功能,Chro ...