前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >YbtOJ 544「后缀自动机」子串选取

YbtOJ 544「后缀自动机」子串选取

作者头像
yzxoi
发布2022-09-19 14:04:12
2740
发布2022-09-19 14:04:12
举报
文章被收录于专栏:OI

YbtOJ 544「后缀自动机」子串选取

题目链接:YbtOJ #544

小 A 有一个长度为 n 的小写字母串 s

你可以从左到右依次 选出 若干个 无交 子串 t_1,t_2,\cdots,t_m,要求每次选出的字符串 t_i 必须是前一个字符串 t_{i-1} 的 真子串(即 t_it_{i-1} 的子串且 t_i 的长度比 t_{i-1} 小)。

小 A 想要知道一次最多能选出多少个子串(即选出的 m 最大是多少)。

1\le n\le5\times10^5

Solution

不妨把串翻转,那么就变成每次在上一个子串的基础上左右任添加字符。

容易发现答案最大只有 \sqrt{2n},并且贪心地想子串的长度必然依次为 1,2,3,\cdots,Ans

f_i 表示当前答案 Ans 下以 i 为末尾的后缀是否可行。

按顺序枚举,转移时判断 [i-Ans+2,i][i-Ans+1,i-1] 的出现情况即可,注意该子串需要为 i-Ans 前的。

注意到 i-Ans 单调不减,所以在每次 Ans 减少时加入原来 i-Ans 为末尾的合法子串。

注意到任意时刻 Hash Table 中有用的字符串长度相同,所以动态维护即可。

据说单模也能过,于是我试着换成了单模结果又wa又T的。

然后这道题 SAM+线段树 也是能做的,只需要一只 \log,但是常数大跑得比根号还慢/cy。

Code

代码语言:javascript
复制
#pragma GCC optimize("Ofast")
#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,avx2,fma")
#pragma GCC optimize("unroll-loops")
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define W while
#define I inline
#define RI register int
#define LL long long
#define Cn const
#define CI Cn int&
using namespace std;
namespace Debug{
    Tp I void _debug(Cn char* f,Ty t){cerr<<f<<'='<<t<<endl;}
    Ts I void _debug(Cn char* f,Ty x,Ar... y){W(*f!=',') cerr<<*f++;cerr<<'='<<x<<",";_debug(f+1,y...);}
    Tp ostream& operator<<(ostream& os,Cn vector<Ty>& V){os<<"[";for(Cn auto& vv:V) os<<vv<<",";os<<"]";return os;}
    #define gdb(...) _debug(#__VA_ARGS__,__VA_ARGS__)
}using namespace Debug;
Cn int N=5e5+10,M=sqrt(2*N);
int n,a[N],OW[2][N],Ans,f[N];char s[N];
struct Base{int pw,md;}o[2];
struct HashX{int x,y;}h[N];
I HashX operator+(Cn HashX& A,CI v){return (HashX){(1LL*A.x*o[0].pw%o[0].md+v)%o[0].md,(1LL*A.y*o[1].pw%o[1].md+v)%o[1].md};}
I bool operator==(Cn HashX& A,Cn HashX& B){return A.x==B.x&&A.y==B.y;}
I HashX Hash(CI l,CI r){return (HashX){(h[r].x+o[0].md-1LL*h[l-1].x*OW[0][r-l+1]%o[0].md)%o[0].md,(h[r].y+o[1].md-1LL*h[l-1].y*OW[1][r-l+1]%o[1].md)%o[1].md};}
#define PA pair<int,int>
#define MP make_pair
#define fi first
#define se second
vector<PA> q[N];
#define pb push_back
I bool operator<(Cn HashX& A,Cn HashX &B){return A.x^B.x?A.x<B.x:A.y<B.y;}
map<HashX,bool> mp[M];
struct HashTable{
    int fir[19260818],nxt[N*4],val[N*4],w[N*4],tot;
    I void Add(Cn HashX& z){nxt[++tot]=fir[z.x],fir[z.x]=tot,w[tot]=z.y;}
    I int Q(Cn HashX& z){RI i;for(i=fir[z.x];i;i=nxt[i]) if(w[i]==z.y) return 1;return 0;}
}H;
int main(){
    freopen("substr.in","r",stdin),freopen("substr.out","w",stdout);
    RI i,j,mx,t;for(scanf("%d",&n),mx=sqrt(2*n),scanf("%s",s+1),o[0]=(Base){233,19260817},o[1]=(Base){31,19260817},i=1;i<=n;i++) a[i]=s[n-i+1]-'a'+1,h[i]=h[i-1]+a[i];
    for(OW[0][0]=OW[1][0]=i=1;i<=n;i++) OW[0][i]=1LL*OW[0][i-1]*o[0].pw%o[0].md,OW[1][i]=1LL*OW[1][i-1]*o[1].pw%o[1].md;for(H.Add((HashX){0,0}),i=1;i<=n;i++){
        for(t=f[i-1]+1;!H.Q(Hash(i-t+2,i))&&!H.Q(Hash(i-t+1,i-1));){
            t--;for(j=f[i-t];j>=1&&!H.Q(Hash(i-t-j+1,i-t));j--) H.Add(Hash(i-t-j+1,i-t));
        }Ans=max(Ans,(f[i]=t));
    }return printf("%d\n",Ans),cerr<<clock()<<'\n',0;
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-02-10 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • YbtOJ 544「后缀自动机」子串选取
    • Solution
      • Code
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档