bzoj 3796: Mushroom追妹纸 AC自动机+后缀自动机+dp
题目大意:
给定三个字符串s1,s2,s3,求一个字符串w满足:
- w是s1的子串
- w是s2的子串
- s3不是w的子串
- w的长度应尽可能大
题解:
首先我们可以用AC自动机找出s3在s1,s2中出现的位置(窝不会kmp)
不完全包括特定区间的最长公共子串了.
我们二分一下答案的长度k
于是我们发现问题变成了:
- 给定两个字符串,有一些点不能选择,问是否存在两个点所代表后缀的LCP >= k
所以我们将两个字符串拼接起来,有后缀自动机建立后缀树
然后在后缀树上O(n)dp一边便可处理
\(O(nlogn)\)
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
inline void read(int &x){
x=0;char ch;bool flag = false;
while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true;
while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;
}
inline int cat_max(const int &a,const int &b){return a>b ? a:b;}
inline int cat_min(const int &a,const int &b){return a<b ? a:b;}
namespace ACM{
const int maxn = 10010;
int ch[maxn][26],nodecnt;
bool danger[maxn];
inline void insert(char *s){
int nw = 0;
for(int i=0,c;s[i] != 0;++i){
c = s[i] - 'a';
if(ch[nw][c] == 0) ch[nw][c] = ++ nodecnt;
nw = ch[nw][c];
}danger[nw] = true;
}
int q[maxn],l,r,fail[maxn];
inline void build(){
l = 0;r = -1;fail[0] = 0;
for(int c=0;c<26;++c){
if(ch[0][c]){
fail[ch[0][c]] = 0;
q[++r] = ch[0][c];
}
}
while(l <= r){
int u = q[l++];
for(int c=0;c<26;++c){
int t = ch[fail[u]][c];
if(ch[u][c] == 0) ch[u][c] = t;
else{
fail[ch[u][c]] = t;
q[++r] = ch[u][c];
}
}
}
}
}
namespace Graph{
const int maxn = 210010;
struct Node{
int to,next;
}G[maxn];
int head[maxn],cnt;
void add(int u,int v){
G[++cnt].to = v;
G[cnt].next = head[u];
head[u] = cnt;
}
}
namespace SAM{
const int maxn = 210010;
struct Node{
int nx[27];
int len,fa,x;
}T[maxn];
int last,nodecnt;
inline void init(){
T[last = nodecnt = 0].fa = -1;
}
inline void insert(char cha,int i){
int c = cha - 'a',cur = ++nodecnt,p;
T[cur].len = T[last].len + 1;
for(p = last;p != -1 && T[p].nx[c] == 0;p = T[p].fa) T[p].nx[c] = cur;
if(p == -1) T[cur].fa = 0;
else{
int q = T[p].nx[c];
if(T[q].len == T[p].len + 1) T[cur].fa = q;
else{
int co = ++ nodecnt;
T[co] = T[q];T[co].len = T[p].len + 1;
for(;p != -1 && T[p].nx[c] == q;p = T[p].fa) T[p].nx[c] = co;
T[cur].fa = T[q].fa = co;
}
}T[last = cur].x = i;
}
}
const int maxn = 210010;
char s1[maxn],s2[maxn],s3[maxn];
bool flag1[maxn],flag2[maxn];
int len1,len2,w,mid;
inline void work1(){
using namespace ACM;
int nw = 0;
for(int i=0;s1[i] != 0;++i){
nw = ch[nw][s1[i] - 'a'];
if(danger[nw]) flag1[i-w+1] = true;
}
nw = 0;
for(int i=0;s2[i] != 0;++i){
nw = ch[nw][s2[i] - 'a'];
if(danger[nw]) flag2[i-w+1] = true;
}
}
bool vis[maxn];
bool le[maxn],ri[maxn],both[maxn];
#define v G[i].to
inline bool dfs(int u,int f){
using namespace Graph;
using namespace SAM;
for(int i = head[u];i;i=G[i].next){
if(v == f) continue;
if(dfs(v,u)) return true;
if(le[v] && ri[v]) both[u] = true;
else le[u] |= le[v],ri[u] |= ri[v];
}
if(!vis[T[u].x]){
if(T[u].x < len1) le[u] = true;
if(T[u].x > len1) ri[u] = true;
}
if(T[u].len >= mid){
if(le[u] && ri[u]) return true;
if(le[u] && both[u]) return true;
if(ri[u] && both[u]) return true;
}
le[u] |= both[u];ri[u] |= both[u];
return false;
}
#undef v
inline bool check(){
for(int i=len1-1,c=0;i>=0;--i){
vis[i] = false;
if(flag1[i]) c = mid - w + 1;
if(c != 0) vis[i] = true,-- c;
}vis[len1] = true;
for(int i=len2-1,c=0;i>=0;--i){
vis[i+len1+1] = false;
if(flag2[i]) c = mid - w + 1;
if(c != 0) vis[i+len1+1] = true,--c;
}
for(int i=0;i<=SAM::nodecnt;++i) le[i] = ri[i] = both[i] = 0;
if(dfs(0,0)) return true;
return false;
}
int main(){
scanf("%s",s1);scanf("%s",s2);scanf("%s",s3);
ACM::insert(s3);ACM::build();
len1 = strlen(s1);len2 = strlen(s2);w = strlen(s3);
work1();SAM::init();
for(int i=len2-1;i>=0;--i) SAM::insert(s2[i],len1+i+1);SAM::insert('z'+1,len1);
for(int i=len1-1;i>=0;--i) SAM::insert(s1[i],i);
for(int i=1;i<=SAM::nodecnt;++i) Graph::add(SAM::T[i].fa,i);
int l = 0,r = len1,ans = 0;
while(l <= r){
mid = (l+r) >> 1;
if(check()) l = mid+1,ans = mid;
else r = mid-1;
}
printf("%d\n",ans);
getchar();getchar();
return 0;
}
bzoj 3796: Mushroom追妹纸 AC自动机+后缀自动机+dp的更多相关文章
- bzoj 3796: Mushroom追妹纸【二分+后缀数组+st表】
把三个串加上ASCII大于z的分隔符连起来,然后求SA 显然每个相同子串都是一个后缀的前缀,所以枚举s1的每个后缀的最长和s2相同的前缀串(直接在排序后的数组里挨个找,最近的两个分别属于s1和s2的后 ...
- [BZOJ 3796]Mushroom追妹纸
[BZOJ 3796]Mushroom追妹纸 题目 Mushroom最近看上了一个漂亮妹纸.他选择一种非常经典的手段来表达自己的心意——写情书.考虑到自己的表达能力,Mushroom决定不手写情书.他 ...
- ●BZOJ 3796 Mushroom追妹纸
题链: http://www.lydsy.com/JudgeOnline/problem.php?id=3796 题解: 题意: 给出三个串 A,B,C 找出一个最长串 S, 使得 ...
- bzoj 3796 Mushroom追妹纸——后缀数组
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3796 长度一般都是 1e5 ,看这个是 5e4 ,一看就是把两个串接起来做. 自己本来想的是 ...
- bzoj 3796 Mushroom追妹纸 —— 后缀数组
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3796 先把三个串拼在一起,KMP 求 s1 , s2 中每个位置和 s3 的匹配情况: 注意 ...
- BZOJ 3796 Mushroom追妹纸 哈希+二分(+KMP)
先把两个串能匹配模式串的位置找出来,然后标记为$1$(标记在开头或末尾都行),然后对标记数组求一个前缀和,这样可以快速查到区间内是否有完整的一个模式串. 然后二分子串(答案)的长度,每次把长度为$md ...
- 【BZOJ3796】Mushroom追妹纸 二分+hash
[BZOJ3796]Mushroom追妹纸 Description Mushroom最近看上了一个漂亮妹纸.他选择一种非常经典的手段来表达自己的心意——写情书.考虑到自己的表达能力,Mushroom决 ...
- [BZOJ3796]Mushroom追妹纸:后缀自动机+KMP
分析 这道题有个\(O(n)\)的后缀自动机做法,感觉很好理解就在这说一下. 先对\(s1\)和\(s2\)求最长公共子串,对于\(s2\)的每一个下标\(i\),求一个\(f[i]\)表示以\(s2 ...
- 【bzoj3796】Mushroom追妹纸 hash/sa+kmp+二分
Description Mushroom最近看上了一个漂亮妹纸.他选择一种非常经典的手段来表达自己的心意--写情书.考虑到自己的表达能力,Mushroom决定不手写情书.他从网上找到了两篇极佳的情书, ...
随机推荐
- ASIHTTP
本文转载至 http://www.th7.cn/Program/IOS/201303/128223.shtml 向服务器端上传数据 ASIFormDataRequest ,模拟 Form表单提 ...
- WPF 支持集合绑定的控件
WPF 支持集合绑定的控件 ListBox ComboBox ListView DataGrid
- 【BZOJ3230】相似子串 后缀数组+二分+RMQ
[BZOJ3230]相似子串 Description Input 输入第1行,包含3个整数N,Q.Q代表询问组数.第2行是字符串S.接下来Q行,每行两个整数i和j.(1≤i≤j). Output 输出 ...
- [Matlab绘图][三维图形][三维曲线基本函数+三维曲面+其他三维图形]
1.绘制三维图形的基本函数 最基本的三维绘图函数为plot3: plot3与plot用法十分相似,调用格式: plot(x1,y1,z1,选项1,x2,y2,z2,选项2,...,xn,yn,zn,选 ...
- Java中List.remove报UnsupportedOperationException异常
今天项目中有个需求场景: A和B都是List,而B是A的子集,现在想求A和B的差集. 想到了List中提供的removeAll()方法可以求得差集,但是结果确报了UnsupportedOperatio ...
- sql 语句实现分页查询
SELECT * FROM ( SELECT A.*, ROWNUM RN FROM (SELECT * FROM TABLE_NAME) A WHERE ROWNUM <= 40 ) WHER ...
- 路由器桥接(WIFI无线中继)设置及摆放位置图解
路由器桥接(WIFI无线中继)设置及摆放位置图解 WIFI实在好用,但它的波覆盖面小.穿透力很差.我们安装时要考虑波的衍射特点,装在衍射效果最佳的位置(居中,室外可绕,避开密封墙).确实无法兼顾的地方 ...
- SAP号码段
[转http://blog.csdn.net/wengyupeng/article/details/8513527] 1.通用Tcode:SNRO 常用维护特定Number range Tcode: ...
- spring启动quartz定时器
在很多中经常要用到定时任务,quartz是定时器中比较好用的,在Spring中使用quartz是很容易的事情,首先在spring的applicationContext.xml文件中增加如下配置: &l ...
- Kattis - friday 【数学】
题意 每一年的第一天 都是星期天 然后 给出 一年的总天数 和 总月数 以及 每个月 的总天数 求出 有多少个星期五 是当月的13号 思路 对于 每个月 只要判断 当月的13号 是不是 星期五 就可以 ...