奇怪的字符串:需要一点观察的 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. HZNUOJ-1503公路乘车--DP

    题目传送门:https://acm.hznu.edu.cn/OJ/problem.php?id=1503 题解:我们发现后一状态由前一状态决定,即后一公里由前面十公里的状态决定,经典 dp,我们直接列 ...

  2. ORCA避障源码笔记

    参考资料 https://gamma.cs.unc.edu/ORCA/publications/ORCA.pdf https://gamma.cs.unc.edu/RVO2/ 数学知识 1.向量的点乘 ...

  3. 《JavaScript 模式》读书笔记(5)— 对象创建模式3

    这一篇,我们主要来学习了解下沙箱模式以及静态成员的相关内容. 五.沙箱模式 沙箱模式(sandbox pattern)解决了命名空间模式的如下几个缺点: 对单个全局变量的依赖变成了对应用程序的全局变量 ...

  4. CVE-2023-0461 漏洞分析与利用

    PS: 文章首发于补天社区 漏洞分析 tcp_set_ulp里面会分配和设置 icsk->icsk_ulp_data,其类型为 tls_context tcp_setsockopt do_tcp ...

  5. 【处理元组有关的题型的技巧】codeforces 1677 A. Tokitsukaze and Strange Inequality

    题意 第一行输入一个正整数 \(T(1 \leq T \leq 1000)\),代表共有 \(T\) 组测试用例,对于每组测试用例: 第一行输入一个正整数 \(n(4 \leq n \leq 5000 ...

  6. 武装你的WEBAPI-OData使用Endpoint

    本文属于 OData 系列文章 Introduction 更新: 由于新版的 OData 已经默认使用了 endpoint 模式(Microsoft.AspNetCore.OData 8.0.0),不 ...

  7. 【Docker】安装镜像报错warning: /var/cache/yum/x86_64/7/extras/packages/epel-release-7-11.noarch.rpm: Header

    这个的解决方法就是加上这个就可以了

  8. Knife4j文档请求异常 app.23f8b31d.js:1 SyntaxError: Unexpected token I in JSON at position 6820

    发现代码里example包含了特殊字符[] 去掉[]即可 @ApiModelProperty(value = "状态", example = "INIT, RUNNING ...

  9. 『玩转Streamlit』--集成Plotly

    之前介绍了如何在Streamlit App中使用Matplotlib库来绘图. 本篇介绍 Steamlit结合Poltly的方法,相比于Matplotlib,Poltly的交互性更强, 更适合在Web ...

  10. Qt/C++编写的mqtt调试助手使用说明

    一.使用说明 第一步,选择协议前缀,可选mqtt://.mqtts://.ws://.wss://四种,带s结尾的是走ssl通信,ws表示走websocket通信.一般选默认的mqtt://就好. 第 ...