jzoj2921. 【NOI2012模拟题】字符串识别(string) & jzoj4372. 【GDOI2016模拟】识别子串 (SAM优化)

16 篇文章 0 订阅
2 篇文章 0 订阅

题目描述

(其实两道题就是数据范围不一样)
Description
XX 在进行字符串研究的时候,遇到了一个十分棘手的问题。在这个问题中,给定一个字符串 S,与一个整数 K,定义 S 的子串 T=S(i, j)是关于第 K 位的识别子串,满足以下两个条件:

1、i≤K≤j。

2、子串 T 只在 S 中出现过一次。

例如,S=”banana”,K=5,则关于第 K 位的识别子串有”nana”,”anan”,”anana”,”nan”,”banan”和”banana”。

现在,给定 S,XX 希望知道对于 S 的每一位,最短的识别子串长度是多少,请你来帮助他。

Input
仅一行,输入长度为 N 的字符串 S。

Output
输出 N 行,每行一个整数,第 i 行的整数表示对于第 i 位的最短识别子串长度。

Sample Input
agoodcookcooksgoodfood
Sample Output
1
2
3
3
2
2
3
3
2
2
3
3
2
1
2
3
3
2
1
2
3
4

Data Constraint

Hint
N<=500000


Description
现在同学们把大多数作业都做完了,但是却被最后一个题给难住了。
一般地,对于一个字符串S,和S中第k个字符,定义子串T=S(i..j)为一个关于k的识别子串,当且仅当
1、i<=k<=j。
2、T在S中只出现一次。
比如,对于banana的第5个字符,“nana”,“anan”,“anana”,“nan”,“banan”和“banana”都是关于它的识别子串。
自然,识别子串越短越好(太长了也就失去意义了),现在请你计算出对于一个字符串S,关于S的每一位的最短识别子串分别有多长。

Input
一行,一个长度为L的字符串S,S只包含小写字母。
Output
L行,每行1个整数,第i行的数表示关于S的第i个元素的最短识别子串有多长。

Sample Input
agoodcookcooksgoodfood
Sample Output
1
2
3
3
2
2
3
3
2
2
3
3
2
1
2
3
3
2
1
2
3
4

Data Constraint
第一个点 L=100
第二个点 L=1000
第三个点 L=5000
第四个点到第十个点 L=100000

100%

直接SAM建图,然后处理出每个位置至少向左扩展多少位才能使其唯一
之后线段树乱搞

一个优化:
往fail链跳时,如果对应的next不是当前所指就退
因为如果一个点的next不同,fail链上的肯定不同

code

#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define max(a,b) (a>b?a:b)
#define min(a,b) (a<b?a:b)
#define Maxlen 1000010
using namespace std;

int next[Maxlen][26];
int c[Maxlen];
int d[Maxlen];
int T[Maxlen];
int sum[Maxlen];
int fail[Maxlen];
int tr[20000000][2];
int mx[Maxlen];
char s[Maxlen];
int f[Maxlen];
int ans[Maxlen];
int C[26];
bool bz[26];
int i,j,k,l,Len,len,last,I,J,K,L,S,S2,ch,h,t,N;

struct B{
    int x,y;
} b[Maxlen];

__attribute__((optimize("-O2")))
bool cmp(B a,B b)
{
    return a.x<b.x;
}

__attribute__((optimize("-O2")))
void down(int t)
{
    if (tr[t][1])
    {
        tr[t][0]=min(tr[t][0],tr[t][1]);
        tr[t*2][1]=min(tr[t*2][1],tr[t][1]);
        tr[t*2+1][1]=min(tr[t*2+1][1],tr[t][1]);

        tr[t][1]=233333333;
    }
}

__attribute__((optimize("-O2")))
void change(register int t,int l,int r,int x,int y,int s)
{
    register int mid=(l+r)/2;

    down(t);
    if (x<=l && r<=y)
    {
        tr[t][1]=s;
        down(t);

        return;
    }

    if (x<=mid)
    change(t*2,l,mid,x,y,s);
    if (mid<y)
    change(t*2+1,mid+1,r,x,y,s);

    tr[t][0]=min(min(tr[t*2][0],tr[t*2][1]),min(tr[t*2+1][0],tr[t*2+1][1]));
}

__attribute__((optimize("-O2")))
void New(int x)
{
    len++;
    T[len]=i+1;
    mx[len]=mx[x]+1;
}

__attribute__((optimize("-O2")))
void bfs()
{
    h=0;
    t=1;
    d[1]=1;

    while (h<t)
    {
        h++;
        fo(i,0,25)
        if (next[d[h]][i])
        {
            if (!f[next[d[h]][i]])
            f[next[d[h]][i]]=f[d[h]]+1;

            c[next[d[h]][i]]--;
            if (!c[next[d[h]][i]])
            d[++t]=next[d[h]][i];
        }
    }
}

__attribute__((optimize("-O2")))
void getans(register int t,int l,int r)
{
    register int mid=(l+r)/2;

    if (l==r)
    {
        tr[t][0]=min(tr[t][0],tr[t][1]);
        tr[t][1]=0;

        printf("%d\n",min(tr[t][0],ans[l]-l+1));

        return;
    }
    else
    down(t);

    getans(t*2,l,mid);
    getans(t*2+1,mid+1,r);
}

__attribute__((optimize("-O2")))
int main()
{
    memset(tr,127,sizeof(tr));

    len=1,last=1;
    mx[1]=0;
    sum[1]=1;
    fail[1]=0;

    i=0;
    s[0]=getchar();
    while (s[i]>='a')
    s[++i]=getchar();
    Len=i-1;

    fo(i,0,Len) bz[s[i]-'a']=1;
    N=-1;
    fo(i,0,25)
    if (bz[i])
    C[i]=++N;

    fo(i,0,Len)
    {
        ch=C[s[i]-'a'];

        New(last);
        next[last][ch]=len;

        K=last;
        last=next[last][ch];

        for (j=fail[K]; j; j=fail[j])
        if (!next[j][ch])
        next[j][ch]=last;
        else
        {
            if ((mx[j]+1)!=mx[next[j][ch]])
            {
                k=next[j][ch];

                New(j);
                fo(l,0,N)
                next[len][l]=next[k][l];

                fail[len]=fail[k];
                fail[last]=len,fail[k]=len;

                for (l=j; next[l][ch]==k; l=fail[l]) //优化
                next[l][ch]=len;
            }
            else
            fail[last]=next[j][ch];

            break;
        }
        if (!j)
        fail[last]=1;

        sum[last]++;
    }

    fo(i,1,len)
    b[i].x=mx[i],b[i].y=i;
    stable_sort(b+1,b+len+1,cmp);

    fd(i,len,1)
    sum[fail[b[i].y]]+=sum[b[i].y];

    fo(i,1,len)
    {
        fo(j,0,N)
        c[next[i][j]]++;
    }
    bfs();

    j=0;
    fo(i,2,len)
    if (sum[i]==1)
    {
        change(1,1,Len+1,T[i]-f[i]+1,T[i],f[i]);

        if (T[i]>f[i])
        {
            j++;
            b[j].x=T[i]-f[i];
            b[j].y=T[i];
        }
    }
    stable_sort(b+1,b+j+1,cmp);

    k=233333333;
    fd(i,Len+1,1)
    {
        while (j && i<=b[j].x)
        {
            k=min(k,b[j].y);
            j--;
        }
        ans[i]=k;
    }

    getans(1,1,Len+1);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值