Luogu P11553 ROIR 2016 Day 1 奇怪的字符串 题解 [ 绿 ] [ 后缀自动机 ] [ 枚举 ] [ 观察 ]
奇怪的字符串:需要一点观察的 SAM 小清新题。
观察
我们首先观察什么样的字符串才是奇怪的,可以发现,首先类似 AAAAAAA 之类全部相等的字符串是奇怪的。
继续观察,如果字符种类变为两种或者三种能不能是奇怪的。显然,类似 AAABBBBCCDDDEEEEEE 之类有三种及以上的且每个字母都只在一个连续段出现的串都不是奇怪的,因为我们一定可以选不相邻的两个连续段出来,是他不是原串的子串。因此,类似 AAAAABBBBBB 的就可以了。
那么一个字母有多个连续段的是不是奇怪的呢?考虑像 AAABBBAA 样子的串,我们显然可以把这些连续段的字母全部选出,例如选出 AAAAA,这样一定不是原来的子串。
因此,一个字符串奇怪,当且仅当它满足类似 AAAAAAAA 或 AAAAAABBBBBBB 的形态。
实现
暴力枚举做法
枚举第二种形态的两个字符,线性扫一遍统计即可。
时间 \(O(n|\sum|^2)\),能过。
SAM 做法
考虑建出 SAM,枚举字符 \(c\),先求出从根节点到每个节点是否有只存在字符 \(c\) 的路径,这个可以通过正向拓扑一遍实现。然后再反向拓扑一遍,记录下每个节点后面最多能接多少个 \(c\),答案统计的时候先统计第一种形态的答案,再统计第二种,把后缀最多能接的字符数加上即可。
具体看代码吧,时间复杂度 \(O(n|\sum|)\),但我实现得很烂,还没暴力枚举跑得快。
代码
#include <bits/stdc++.h>
#define fi first
#define se second
#define lc (p<<1)
#define rc ((p<<1)|1)
#define eb(x) emplace_back(x)
#define pb(x) push_back(x)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ldb;
using pi=pair<int,int>;
char s[200005];
ll ans;
int n,np=1,tot=1,ch[400005][26],fa[400005],len[400005],rd[400005];
ll pre[400005][26],suf[400005][26],sm[400005];
vector<pi>g1[400005],g2[400005];
void extend(int c)
{
int p=np;
np=++tot;
len[np]=len[p]+1;
for(;p&&ch[p][c]==0;p=fa[p])ch[p][c]=np;
if(p==0)fa[np]=1;
else
{
int q=ch[p][c];
if(len[q]==len[p]+1)fa[np]=q;
else
{
int nq=++tot;
len[nq]=len[p]+1;
fa[nq]=fa[q],fa[q]=nq,fa[np]=nq;
for(;p&&ch[p][c]==q;p=fa[p])ch[p][c]=nq;
memcpy(ch[nq],ch[q],sizeof(ch[q]));
}
}
}
void topo1(vector<pi>*g,ll dp[400005][26])
{
memset(rd,0,sizeof(rd));
for(int i=1;i<=tot;i++)
{
for(auto ed:g[i])
{
int v=ed.fi;
rd[v]++;
}
}
queue<int>q;
for(int i=1;i<=tot;i++)
{
if(rd[i]==0)q.push(i);
}
while(!q.empty())
{
int u=q.front();
q.pop();
for(auto ed:g[u])
{
int v=ed.fi,c=ed.se;
rd[v]--;
if(rd[v]==0)q.push(v);
if(dp[u][c]>=len[u])dp[v][c]+=dp[u][c]+1;
}
}
}
void topo2(vector<pi>*g,ll dp[400005][26])
{
memset(rd,0,sizeof(rd));
for(int i=1;i<=tot;i++)
{
for(auto ed:g[i])
{
int v=ed.fi;
rd[v]++;
}
}
queue<int>q;
for(int i=1;i<=tot;i++)
{
if(rd[i]==0)q.push(i);
}
while(!q.empty())
{
int u=q.front();
q.pop();
for(auto ed:g[u])
{
int v=ed.fi,c=ed.se;
rd[v]--;
if(rd[v]==0)q.push(v);
dp[v][c]+=dp[u][c]+1;
}
}
}
void cal()
{
for(int i=1;i<=tot;i++)
{
for(int j=0;j<26;j++)
{
pre[i][j]=pre[i][j];
ans+=(pre[i][j]>0);
sm[i]+=pre[i][j];
}
}
for(int i=1;i<=tot;i++)
{
for(int j=0;j<26;j++)
{
ans+=suf[i][j]*((sm[i]-pre[i][j])>0);
}
}
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>s+1;
n=strlen(s+1);
for(int i=1;i<=n;i++)extend(s[i]-'a');
for(int i=1;i<=tot;i++)
{
for(int j=0;j<26;j++)
{
int v=ch[i][j];
if(v)
{
g1[i].push_back({v,j});
g2[v].push_back({i,j});
}
}
}
topo1(g1,pre);
topo2(g2,suf);
cal();
cout<<ans;
return 0;
}
Luogu P11553 ROIR 2016 Day 1 奇怪的字符串 题解 [ 绿 ] [ 后缀自动机 ] [ 枚举 ] [ 观察 ]的更多相关文章
- Luogu P3346 [ZJOI2015]诸神眷顾的幻想乡 广义SAM 后缀自动机
题目链接 \(Click\) \(Here\) 真的是好题啊-不过在说做法之前先强调几个自己总是掉的坑点. 更新节点永远记不住往上跳\(p = fa[p]\) 新建节点永远记不住\(len[y] = ...
- [AHOI2004]奇怪的字符串
[AHOI2004]奇怪的字符串 题目描述 输入输出格式 输入格式: 输入文件中包含两个字符串X和Y.当中两字符串非0即1.序列长度均小于9999. 输出格式: X和Y的最长公共子序列长度. 输入输出 ...
- 洛谷—— P2543 [AHOI2004]奇怪的字符串
P2543 [AHOI2004]奇怪的字符串 题目描述 输入输出格式 输入格式: 输入文件中包含两个字符串X和Y.当中两字符串非0即1.序列长度均小于9999. 输出格式: X和Y的最长公共子序列长度 ...
- BZOJ 2806 Luogu P4022 [CTSC2012]Cheat (广义后缀自动机、DP、二分、单调队列)
题目链接: (bzoj) https://www.lydsy.com/JudgeOnline/problem.php?id=2806 (luogu) https://www.luogu.org/pro ...
- BZOJ 4032 Luogu P4112 [HEOI2015]最短不公共子串 (DP、后缀自动机)
这其实是道水题... 题目链接: (bzoj)https://www.lydsy.com/JudgeOnline/problem.php?id=4032 (luogu)https://www.luog ...
- 洛谷 P1852 奇怪的字符串
P1852 奇怪的字符串 题目描述 输入两个01串,输出它们的最长公共子序列的长度 输入输出格式 输入格式: 一行,两个01串 输出格式: 最长公共子序列的长度 输入输出样例 输入样例#1: 复制 0 ...
- UOJ #395 BZOJ 5417 Luogu P4770 [NOI2018]你的名字 (后缀自动机、线段树合并)
NOI2019考前做NOI2018题.. 题目链接: (bzoj) https://www.lydsy.com/JudgeOnline/problem.php?id=5417 (luogu) http ...
- [Luogu P3295][SCOI 2016]萌萌哒
先说下暴力做法,如果[l1,r1]和[l2,r2]子串相等等价于两个区间内每个数对应相等.那么可以用并查集暴力维护,把对应相等的数的位置维护到同一个集合里去,最后答案其实就是把每个集合可以放的数个数乘 ...
- Luogu 4784 [BalticOI 2016 Day2]城市
斯坦纳树复习,我暑假的时候好像写过[JLOI2015]管道连接来着. 设$f_{i, s}$表示以$i$为根,$k$个重要点的连通状态为$s$,($0$代表没有连进最小生成树里面去,$1$代表连进了最 ...
- [LUOGU] P2543 [AHOI2004]奇怪的字符串
LCS //Writer:GhostCai && His Yellow Duck #include<iostream> #include<cstring> #d ...
随机推荐
- 深度变分信息瓶颈——Deep Variational Information Bottleneck
Deep Variational Information Bottleneck (VIB) 变分信息瓶颈 论文阅读笔记.本文利用变分推断将信息瓶颈框架适应到深度学习模型中,可视为一种正则化方法. 变分 ...
- 鸿蒙NEXT开发案例:保质期计算
[引言] 保质期计算应用是一个基于鸿蒙NEXT框架开发的数字和文本统计组件.用户可以输入商品的生产日期和保质期天数,应用会自动计算并展示相关信息,包括保质状态.剩余天数.生产日期和到期日期. [环境准 ...
- 使用Flex布局的几个小技巧
前情 Flex 是 Flexible Box 的缩写,意为"弹性布局",用来为盒状模型提供最大的灵活性,任何一个容器都可以指定为 Flex 布局,如果说目前我开发中离不开的布局方式 ...
- uni-app项目分包后子包中静态资源丢失
前情 uni-app是我比较喜欢的跨平台框架,它能开发小程序/H5/APP(安卓/iOS),重要的是对前端开发友好,自带的IDE让开发体验非常棒,公司项目就是主推uni-app. 坑位 随着项目越做越 ...
- uni-app中使用svg
标签: svg js uni-app 前情 uni-app是我很喜欢的跨平台框架,它能开发小程序,H5,APP(安卓/iOS),对前端开发很友好,自带的IDE让开发体验也很棒,公司项目就是主推uni- ...
- VTK 9.2 Qt 5.14 安装及错误处理
参考VTK9.1.0在Windows10+VS2019+Qt 5.15.2环境下编译安装以及VTK应用于QT_vtk-qt安装包_isongxw的博客-CSDN博客 安装注意:编译release和de ...
- 鸿蒙NEXT开发案例:颜文字搜索器
[引言] 本文将介绍一个名为"颜文字搜索器"的开发案例,该应用是基于鸿蒙NEXT平台构建的,旨在帮助用户快速查找和使用各种风格的表情符号.通过本案例的学习,读者可以了解如何在鸿蒙平 ...
- node 使用 pm2-logrotate 分割pm2日志 && 停止 pm2-logroatate
使用pm2-logrotate 解决pm2日志体积过大,进行分割 什么是pm2-logrotate? pm2-logrotate 是一个pm2的插件,可以对pm2日志进行管理,所以它的运行需要依靠pm ...
- M1芯片pod问题
M1芯片pod问题 换了M1芯片的mac后,在Xcode跑项目报pod错误,提示run pod install更新pod,但是去终端跑命令时又报错 然后在github上看到一个老哥的方法 https: ...
- ASP.NET Core 中的 Request Feature
ASP.NET Core 中的 Request Feature https://docs.microsoft.com/en-us/aspnet/core/fundamentals/request-fe ...