奇怪的字符串:需要一点观察的 SAM 小清新题。

观察

我们首先观察什么样的字符串才是奇怪的,可以发现,首先类似 AAAAAAA 之类全部相等的字符串是奇怪的。

继续观察,如果字符种类变为两种或者三种能不能是奇怪的。显然,类似 AAABBBBCCDDDEEEEEE 之类有三种及以上的且每个字母都只在一个连续段出现的串都不是奇怪的,因为我们一定可以选不相邻的两个连续段出来,是他不是原串的子串。因此,类似 AAAAABBBBBB 的就可以了。

那么一个字母有多个连续段的是不是奇怪的呢?考虑像 AAABBBAA 样子的串,我们显然可以把这些连续段的字母全部选出,例如选出 AAAAA,这样一定不是原来的子串。

因此,一个字符串奇怪,当且仅当它满足类似 AAAAAAAAAAAAAABBBBBBB 的形态

实现

暴力枚举做法

枚举第二种形态的两个字符,线性扫一遍统计即可。

时间 \(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 奇怪的字符串 题解 [ 绿 ] [ 后缀自动机 ] [ 枚举 ] [ 观察 ]的更多相关文章

  1. Luogu P3346 [ZJOI2015]诸神眷顾的幻想乡 广义SAM 后缀自动机

    题目链接 \(Click\) \(Here\) 真的是好题啊-不过在说做法之前先强调几个自己总是掉的坑点. 更新节点永远记不住往上跳\(p = fa[p]\) 新建节点永远记不住\(len[y] = ...

  2. [AHOI2004]奇怪的字符串

    [AHOI2004]奇怪的字符串 题目描述 输入输出格式 输入格式: 输入文件中包含两个字符串X和Y.当中两字符串非0即1.序列长度均小于9999. 输出格式: X和Y的最长公共子序列长度. 输入输出 ...

  3. 洛谷—— P2543 [AHOI2004]奇怪的字符串

    P2543 [AHOI2004]奇怪的字符串 题目描述 输入输出格式 输入格式: 输入文件中包含两个字符串X和Y.当中两字符串非0即1.序列长度均小于9999. 输出格式: X和Y的最长公共子序列长度 ...

  4. BZOJ 2806 Luogu P4022 [CTSC2012]Cheat (广义后缀自动机、DP、二分、单调队列)

    题目链接: (bzoj) https://www.lydsy.com/JudgeOnline/problem.php?id=2806 (luogu) https://www.luogu.org/pro ...

  5. BZOJ 4032 Luogu P4112 [HEOI2015]最短不公共子串 (DP、后缀自动机)

    这其实是道水题... 题目链接: (bzoj)https://www.lydsy.com/JudgeOnline/problem.php?id=4032 (luogu)https://www.luog ...

  6. 洛谷 P1852 奇怪的字符串

    P1852 奇怪的字符串 题目描述 输入两个01串,输出它们的最长公共子序列的长度 输入输出格式 输入格式: 一行,两个01串 输出格式: 最长公共子序列的长度 输入输出样例 输入样例#1: 复制 0 ...

  7. UOJ #395 BZOJ 5417 Luogu P4770 [NOI2018]你的名字 (后缀自动机、线段树合并)

    NOI2019考前做NOI2018题.. 题目链接: (bzoj) https://www.lydsy.com/JudgeOnline/problem.php?id=5417 (luogu) http ...

  8. [Luogu P3295][SCOI 2016]萌萌哒

    先说下暴力做法,如果[l1,r1]和[l2,r2]子串相等等价于两个区间内每个数对应相等.那么可以用并查集暴力维护,把对应相等的数的位置维护到同一个集合里去,最后答案其实就是把每个集合可以放的数个数乘 ...

  9. Luogu 4784 [BalticOI 2016 Day2]城市

    斯坦纳树复习,我暑假的时候好像写过[JLOI2015]管道连接来着. 设$f_{i, s}$表示以$i$为根,$k$个重要点的连通状态为$s$,($0$代表没有连进最小生成树里面去,$1$代表连进了最 ...

  10. [LUOGU] P2543 [AHOI2004]奇怪的字符串

    LCS //Writer:GhostCai && His Yellow Duck #include<iostream> #include<cstring> #d ...

随机推荐

  1. Javascript 标签的属性

    1.为HTML标签设置和添加属性 setAttribute() setAttribute()方法可以给HTML标签设置/添加属性(原生的属性或者自定义的属性都可以)添加的属性会存储在标签中 <! ...

  2. OceanBase 的探索与实践

    作者:来自 vivo 互联网数据库团队- Xu Shaohui 本文总结了目前我们遇到的痛点问题并通过 OceanBase 的技术方案解决了这些痛点问题,完整的描述了 OceanBase 的实施落地, ...

  3. redmine部署,踩坑而过

    背景:部门想用个工具来做项目执行进度的管理,为了保证数据私有并且不想花钱,选了redmine. 环境:阿里云服务器,windows server R2企业版 软件版本构成: 官方版本说明http:// ...

  4. COSBrowser 文件对比——更实用的文件管理功能

    我们在使用 COSBrowser 来管理腾讯云存储的文件时,目前我们大家所熟知的上传/下载方式,主要有以下三种: 通过点击按钮上传/下载 通过拖拽的形式进行上传/下载 通过 URL 链接进行上传/下载 ...

  5. containerd 导入镜像

    containerd 导入镜像 containerd而非docker,需要离线导入镜像 解决原理https://segmentfault.com/a/1190000019534913ctr --nam ...

  6. 加入security+jwt安全策略

    Pom中引入 <!-- security --> <dependency> <groupId>org.springframework.boot</groupI ...

  7. 创建没有构造函数的NumberFormat

    我有以下课程: import java.text.NumberFormat; public static class NF { public static NumberFormat formatSha ...

  8. Qt编写视频监控系统77-Onvif组件支持非正常时间的设备

    一.前言 在经历了大量的现场设备测试,至少几十种厂家.几百种设备,遇见过奇奇怪怪的问题,一个个想方设法解决,发现有个问题是在下发鉴权的时候,需要带上设备的时间,而不是发送端的时间,如果带的不是设备上的 ...

  9. Qt编写视频监控系统73-不同视频流不同类型的判断和解析(http/m3u8/rtsp/rtmp等)

    一.前言 这套视频监控系统大概从2018年起步整体框架,一步步积累到现在,中间经历了无数次的各种视频文件.视频流.视频设备的播放测试,比如光视频文件就有mp4/wmv/rmvb/mkv/avi等格式, ...

  10. Unix和Windows操作系统中路径中的正斜杠和反斜杠的区别