zoukankan      html  css  js  c++  java
  • BZOJ1396 识别子串 和 BZOJ2865 字符串识别

    字符串识别

    2865: 字符串识别

    Time Limit: 10 Sec  Memory Limit: 128 MB
    Submit: 839  Solved: 261
    [Submit][Status][Discuss]

    Description

    XX在进行字符串研究的时候,遇到了一个十分棘手的问题。

    在这个问题中,给定一个字符串S,与一个整数K,定义S的子串T=S(i, j)是关于第K位的识别子串,满足以下两个条件:

    1、iKj

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

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

    现在,给定SXX希望知道对于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

    HINT

    N<=5*10^5

    Source

    [Submit][Status][Discuss]
    
    HOME Back

    jklover的题解

    SAM + 线段树.

    先建出 parent 树,按照题意,我们只需要处理 right 集合大小为 1 的节点.
    如下图,先算出这样的一个节点合法长度的 max,min ( min 可以用 max(fa)+1 计算).

    ![](https://jkloverdcoi.github.io/2019/04/27/bzoj-1396-%E8%AF%86%E5%88%AB%E5%AD%90%E4%B8%B2/1.png)
    那么区域 I 内每个点的贡献就是区域 II 的长度加上这个点到区域 II 的距离. 区域 II 内每个点的贡献就是区间 II 的长度.开两颗线段树分别修改就可以了.

    如何维护区域1呢?考虑他的答案是pos[u]-i+1,直接更新pos[u],那么把最终线段树上的值-i就是答案了。这种操作比较常见。

    时间复杂度(O(n log n))

    co int N=2e5,INF=0x3f3f3f3f;
    struct node{int min,tag;};
    #define lc (x<<1)
    #define rc (x<<1|1)
    struct SegTree{
    	node t[N*4];
    	void pushup(int x){
    		t[x].min=min(t[lc].min,t[rc].min);
    	}
    	void modify(int x,int v){
    		t[x].min=min(t[x].min,v),t[x].tag=min(t[x].tag,v);
    	}
    	void pushdown(int x){
    		if(t[x].tag<INF){
    			modify(lc,t[x].tag),modify(rc,t[x].tag);
    			t[x].tag=INF;
    		}
    	}
    	void build(int x,int l,int r){
    		t[x].min=t[x].tag=INF;
    		if(l==r) return;
    		int mid=l+r>>1;
    		build(lc,l,mid),build(rc,mid+1,r);
    	}
    	void update(int x,int l,int r,int ql,int qr,int v){
    		if(ql<=l&&r<=qr) return modify(x,v);
    		pushdown(x);
    		int mid=l+r>>1;
    		if(ql<=mid) update(lc,l,mid,ql,qr,v);
    		if(qr>mid) update(rc,mid+1,r,ql,qr,v);
    		pushup(x);
    	}
    }T1,T2;
    int ans[N];
    void query(int x,int l,int r){
    	if(l==r) return ans[l]=min(T1.t[x].min-l,T2.t[x].min),void();
    	T1.pushdown(x),T2.pushdown(x);
    	int mid=l+r>>1;
    	query(lc,l,mid),query(rc,mid+1,r);
    }
    // SAM
    char buf[N];
    int n,tot=1,last=1;
    int ch[N][26],fa[N],len[N],pos[N],siz[N];
    void extend(int c,int po){
    	int p=last,cur=last=++tot;
    	len[cur]=len[p]+1,pos[cur]=po,siz[cur]=1;
    	for(;p&&!ch[p][c];p=fa[p]) ch[p][c]=cur;
    	if(!p) fa[cur]=1;
    	else{
    		int q=ch[p][c];
    		if(len[q]==len[p]+1) fa[cur]=q;
    		else{
    			int clone=++tot;
    			memcpy(ch[clone],ch[q],sizeof ch[q]);
    			fa[clone]=fa[q],len[clone]=len[p]+1,pos[clone]=pos[q];
    			fa[q]=fa[cur]=clone;
    			for(;ch[p][c]==q;p=fa[p]) ch[p][c]=clone;
    		}
    	}
    }
    int cnt[N],ord[N];
    int main(){
    	scanf("%s",buf+1),n=strlen(buf+1);
    	for(int i=1;i<=n;++i) extend(buf[i]-'a',i);
    	for(int i=1;i<=tot;++i) ++cnt[len[i]];
    	for(int i=1;i<=n;++i) cnt[i]+=cnt[i-1];
    	for(int i=1;i<=tot;++i) ord[cnt[len[i]]--]=i;
    	T1.build(1,1,n),T2.build(1,1,n);
    	for(int i=tot;i>=1;--i){
    		int u=ord[i];
    		siz[fa[u]]+=siz[u];
    		if(u==1||siz[u]>1) continue;
    		int l=pos[u]-len[u]+1,r=pos[u]-len[fa[u]];
    		if(l<=r-1) T1.update(1,1,n,l,r-1,pos[u]+1);
    		T2.update(1,1,n,r,pos[u],len[fa[u]]+1);
    	}
    	query(1,1,n);
    	for(int i=1;i<=n;++i) printf("%d
    ",ans[i]);
    	return 0;
    }
    
  • 相关阅读:
    VB字符串分割为数组,并获取下标值
    VB字符串分割为数组并遍历下标值
    VB去除字符串中的字符.
    VB中case用法
    Win10打开运行的快捷键
    SQL Server新建LinkServer
    SQL Server 存储过程之like赋值
    奋战杭电ACM(DAY5)1007
    ACM必备(学完一个就加亮一个)
    奋战杭电ACM(DAY4)1005
  • 原文地址:https://www.cnblogs.com/autoint/p/10805402.html
Copyright © 2011-2022 走看看