【BZOJ1396】识别子串&【BZOJ2865】字符串识别(后缀自动机)

【BZOJ1396】识别子串&【BZOJ2865】字符串识别(后缀自动机)

题面

自从有了DBZOJ
终于有地方交权限题了

题解

很明显,只出现了一次的串
在\(SAM\)的\(right/endpos\)集合大小一定为\(1\)
换句话说,在\(parent\)树上是叶子节点
找到所有这样的节点,
假设它的\(len=r\),它父亲的\(len=p\),它的结束位置为显然就是\(r\)
令\(l=r-p\)
以\(r\)结尾,
并且只出现了一次的串的左端点
为\(1..l\),那么,他们的答案可以更新为\(r+1-i\)
剩下的位置\(l+1..r\),他们无法作为左端点,只能包含在这些串中
于是找到一个最短的包含他们的串\(S[l..r]\)
所以,这段区间的答案可以更新为\(r-l+1\)

显然不好一起维护,
于是开两棵线段树,一个维护\(r+1-i\),先不考虑\(i\),最后减去就好
另一个直接维护\(r-l+1\)

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<queue>
using namespace std;
#define ll long long
#define RG register
#define lson (now<<1)
#define rson (now<<1|1)
#define MAX 111111
inline int read()
{
    RG int x=0,t=1;RG char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=-1,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return x*t;
}
int n;
bool vis[MAX<<1];
struct SAM
{
    struct Node
    {
        int son[26];
        int ff,len;
    }t[MAX<<1];
    int last,tot;
    void init(){last=tot=1;}
    void extend(int c)
    {
        int p=last,np=++tot;last=np;
        t[np].len=t[p].len+1;
        while(p&&!t[p].son[c])t[p].son[c]=np,p=t[p].ff;
        if(!p)t[np].ff=1;
        else
        {
            int q=t[p].son[c];
            if(t[q].len==t[p].len+1)t[np].ff=q;
            else
            {
                int nq=++tot;
                t[nq]=t[q];t[nq].len=t[p].len+1;
                t[np].ff=t[q].ff=nq;
                while(p&&t[p].son[c]==q)t[p].son[c]=nq,p=t[p].ff;
            }
        }
    }
}SAM;
char ch[MAX];
struct SegMentTree
{
    struct Node{int v;}t[MAX<<2];
    void Build(int now,int l,int r)
    {
        t[now].v=1e9;if(l==r)return;
        int mid=(l+r)>>1;
        Build(lson,l,mid);Build(rson,mid+1,r);
    }
    void puttag(int now,int w){t[now].v=min(t[now].v,w);}
    void pushdown(int now)
    {
        if(t[now].v==1e9)return;
        puttag(lson,t[now].v);puttag(rson,t[now].v);
        t[now].v=1e9;
    }
    void Modify(int now,int l,int r,int L,int R,int w)
    {
        if(L>R)return;
        if(L<=l&&r<=R){puttag(now,w);return;}
        pushdown(now);int mid=(l+r)>>1;
        if(L<=mid)Modify(lson,l,mid,L,R,w);
        if(R>mid)Modify(rson,mid+1,r,L,R,w);
    }
    int Query(int now,int l,int r,int p)
    {
        if(l==r)return t[now].v;
        pushdown(now);int mid=(l+r)>>1;
        if(p<=mid)return Query(lson,l,mid,p);
        else return Query(rson,mid+1,r,p);
    }
}A,B;
int main()
{
    SAM.init();
    scanf("%s",ch+1);n=strlen(ch+1);
    for(int i=1;i<=n;++i)SAM.extend(ch[i]-97);
    for(int i=1;i<=SAM.tot;++i)vis[SAM.t[i].ff]=true;
    A.Build(1,1,n);B.Build(1,1,n);
    for(int i=1;i<=SAM.tot;++i)
        if(!vis[i])
        {
            int l=SAM.t[i].len-SAM.t[SAM.t[i].ff].len,r=SAM.t[i].len;
            A.Modify(1,1,n,l,r,r-l+1);
            B.Modify(1,1,n,1,l-1,r+1);
        }
    for(int i=1;i<=n;++i)
        printf("%d\n",min(A.Query(1,1,n,i),B.Query(1,1,n,i)-i));
    return 0;
}

原文地址:https://www.cnblogs.com/cjyyb/p/8695350.html

时间: 2024-10-11 02:53:25

【BZOJ1396】识别子串&【BZOJ2865】字符串识别(后缀自动机)的相关文章

51nod1469 淋漓字符串(后缀自动机)

题目大意: 首先,我们来定义一下淋漓尽致子串. 1.令原串为S. 2.设子串的长度为len,在原串S中出现的次数为k,令其出现的位置为p1, p2, ....pk(即这个子串在原串中[pi,pi + len - 1]中出现). 3.若k=1,则该子串不是淋漓尽致子串. 4.若存在pi,pj(i != j),使得S[pi - 1] = S[pj - 1],则该子串不是淋漓尽致子串. 5.若存在pi,pj(i != j),使得S[pi + len] = S[pj + len],则该字串不是淋漓尽致字

不在B中的A的子串数量 HDU - 4416 (后缀自动机模板题目)

题目: 给定一个字符串a,又给定一系列b字符串,求字符串a的子串不在b中出现的个数. 题解: 先将所有的查询串放入后缀自动机(每次将sam.last=1)(算出所有子串个数) 然后将母串放入后缀自动机然后记录这个子串个数 两个值相减即可 1 #include <set> 2 #include <map> 3 #include <stack> 4 #include <queue> 5 #include <cmath> 6 #include <

字符串(后缀自动机):NOI 2016 优秀的拆分

[问题描述] 如果一个字符串可以被拆分为 AABB 的形式,其中 A 和 B 是任意非空字符串, 则我们称该字符串的这种拆分是优秀的. 例如,对于字符串 aabaabaa,如果令 A = aab, B = a, 我们就找到了这个字符串拆分成 AABB 的一种方式. 一个字符串可能没有优秀的拆分,也可能存在不止一种优秀的拆分. 比如我们令 A = a, B = baa,也可以用 AABB 表示出上述字符串:但是,字符串abaabaa 就没有优秀的拆分. 现在给出一个长度为 n 的字符串 S,我们需

字符串(后缀自动机):Ahoi2013 差异

Description Input 一行,一个字符串S Output 一行,一个整数,表示所求值 Sample Input cacao Sample Output 54 HINT 2<=N<=500000,S由小写英文字母组成 建反向前缀树,O(N)dp求解. 1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 using namespace std; 5 const int N=

字符串(后缀自动机):HDU 4622 Reincarnation

Reincarnation Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 131072/65536 K (Java/Others)Total Submission(s): 3194    Accepted Submission(s): 1184 Problem Description Now you are back,and have a task to do: Given you a string s consist of lo

字符串(后缀自动机):COGS 2399. 循环同构

这道题直接看代码吧. 1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 using namespace std; 5 const int maxn=2000010; 6 int fa[maxn],len[maxn],rit[maxn],w[maxn],sa[maxn]; 7 int n,Q,cnt,lst,ch[maxn][26],vis[maxn]; 8 char s[maxn]; 9 s

[BZOJ1396]识别子串 后缀自动机+线段树

1396: 识别子串 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 451  Solved: 290[Submit][Status][Discuss] Description Input 一行,一个由小写字母组成的字符串S,长度不超过10^5 Output L行,每行一个整数,第i行的数据表示关于S的第i个元素的最短识别子串有多长. Sample Input agoodcookcooksgoodfood Sample Output 1 2 3 3

后缀自动机详解 -----转载

转载于:http://blog.csdn.net/qq_35649707/article/details/66473069 原论文(俄文)地址:suffix_automata 后缀自动机 后缀自动机(单词的有向无环图)——是一种强有力的数据结构,让你能够解决许多字符串问题. 例如,使用后缀自动机可以在某一字符串中搜索另一字符串的所有出现位置,或者计算不同子串的个数——这都能在线性 时间内解决. 直觉上,后缀自动机可以被理解为所有子串的简明信息.一个重要的事实是,后缀自动机以压缩后的形式包含了一个

『后缀自动机入门 SuffixAutomaton』

本文的图片材料多数来自\(\mathrm{hihocoder}\)中详尽的\(SAM\)介绍,文字总结为原创内容. 确定性有限状态自动机 DFA 首先我们要定义确定性有限状态自动机\(\mathrm{DFA}\),一个有限状态自动机可以用一个五元组\((\mathrm{S},\Sigma,\mathrm{st},\mathrm{end},\delta)\)表示,他们的含义如下: \(1.\) \(\mathrm{S}\) 代表自动机的状态集 \(2.\) \(\Sigma\) 代表字符集,也称字

【后缀自动机】资料+个人见解

[资料] 后缀自动机实质上是字母树,记录的字符串是某个字符串s的所有后缀.这里以字符串ACADD为例: 这样很浪费空间和时间(实际上都是O(n^2)).但是,注意:这棵字母树的结点虽然多,但大部分结点都只有一个儿子,而且有很多段是一样的.那么,利用公共部分,就可以对空间进行压缩,具体地说,就是把自己连到儿子的边删掉(并把该儿子及其后代删掉),再把这条边连到别的子树,这样就能充分利用公共部分,节省空间.但是,如何保证这样做和原来的笨做法是等价的,又如何把时间复杂度和空间复杂度降到O(n)?这是个问