bzoj1396: 识别子串

本文介绍了bzoj1396题目,探讨如何识别字符串中的子串问题,主要涉及字符串处理和算法应用。
摘要由CSDN通过智能技术生成

bzoj1396: 识别子串

1396: 识别子串


Description

 

Solution
只出现一次的点只能是叶子。
假设某一个叶子表示的区间是[1-Max]
它的父亲的长度是M。 也就是[Max-M,Max]只出现过一次。
那么我们可以用这M+1长来更新[Max-M,Max]。----A
剩下[1,Max-M]的位置x,可以用[x,Max]这段区间的长度来更新。---B
于是按下标开两棵线段树,一棵维护权值,实现A,一棵维护位置,实现B
然后就好啦
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#define maxn 200005
using namespace std;
int n,cnt=1,rt=1,la=1,w[maxn],tr[maxn*20][2],root[maxn],tot,ans;
int num[maxn];
char ch[maxn];
struct node{
    int Max,par,nex[26],id;    
}s[maxn];
vector<int>G[maxn];
void ins(int c,int id){
    int np=++cnt,p=la;la=np;
    s[np].Max=s[p].Max+1;s[np].id=id;
    for(;p&&!s[p].nex[c];p=s[p].par)s[p].nex[c]=np;
    if(!p)s[np].par=rt;
    else {
        int q=s[p].nex[c];
        if(s[q].Max==s[p].Max+1)s[np].par=q;
        else {
            int nq=++cnt;
            s[nq].Max=s[p].Max+1;s[nq].par=s[q].par;
            for(int i=0;i<26;i++)s[nq].nex[i]=s[q].nex[i];
            s[q].par=s[np].par=nq;
            for(;s[p].nex[c]==q;p=s[p].par)s[p].nex[c]=nq;
        }
    }
} 


int get(int v,int w){
    return (v&(1<<w))>0;
}
void add(int &R,int v){
    if(!R)R=++tot;
    int k=R;
    for(int i=20;i>=0;i--){
        int p=get(v,i);
        if(!tr[k][p])tr[k][p]=++cnt;
        k=tr[k][p];
    }
}
int merge(int x,int y){
    if(!x||!y)return x+y;
    tr[x][0]=merge(tr[x][0],tr[y][0]);
    tr[x][1]=merge(tr[x][1],tr[y][1]);
    return x;
}
int query(int x,int y,int d,int A){
    if(d<0)return A;
    int v=0;
    if(tr[x][0]&&tr[y][1]){
        v=max(v,query(tr[x][0],tr[y][1],d-1,A+(1<<d)));
    }
    if(tr[x][1]&&tr[y][0]){
        v=max(v,query(tr[x][1],tr[y][0],d-1,A+(1<<d)));
    }
    if(v)return v;
    if(tr[x][0]&&tr[y][0]){
        v=max(v,query(tr[x][0],tr[y][0],d-1,A));
    }
    if(tr[x][1]&&tr[y][1]){
        v=max(v,query(tr[x][1],tr[y][1],d-1,A));
    }
    return v;
}
void dfs(int k){
    for(int i=0;i<G[k].size();i++){
        dfs(G[k][i]);
        root[k]=merge(root[k],root[G[k][i]]);
        num[k]+=num[G[k][i]];
    }
    if(s[k].id!=0)add(root[k],w[s[k].id]),num[k]++;
    if(num[k]>1)ans=max(ans,query(root[k],root[k],20,0)+s[k].Max);
}
int main()
{    
    cin>>n;scanf("%s",ch+1);
    for(int i=1;i<=n;i++)scanf("%d",&w[i]);
    for(int i=n;i>=1;i--)ins(ch[i]-'a',i);
    for(int i=1;i<=cnt;i++)G[s[i].par].push_back(i);
    dfs(rt);
    cout<<ans<<endl;
    return 0;
}
View Code

 

posted @ 2019-04-06 21:35 liankewei123456 阅读( ...) 评论( ...) 编辑 收藏
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值