CF 319D Have You Ever Heard About the Word?

Description

String专题又出现啦~(≧▽≦)/~ 这次的题很水的啦0.0

一个字符串的子串是该字符串的一段连续子序列,如bca是abcabc的子串,而cc不是。一个重复块(repeating block)由一个字符串与自身连接而成,如abcabc是一个重复块,而abcabd, ababab不是。

你有一个由拉丁字符组成的字符串。每一步你要找到它的子串中最短的重复块,如果有多于一个,你必须选择最左边的那个。你要将那个形如XX(X - 某个字符串)的重复块替换成X,换句话说你要删除其中的一个X。重复以上步骤直到字符串中不存在重复块。

最终的字符串会是怎样的?看样例解释来更清楚地理解问题描述。

Sample Input

aaaabaaab

Sample Output

ab

Data Constraint

对于10%的数据|S|<=10。
对于30%的数据|S|<=1000。
对于100%的数据1<=|S|<=50000。

Solution

对于这道题我们考虑用hash来判断两个子串是否相同。所以我们先枚举重复串的长度i,显然,若两个长度为i的重复串放在一起,他们肯定会经过(k*i,(k+1) *i),所以我们花O( N/ilogN )的时间去计算相邻两个(k*i,(k+1) *i)的最长公共前缀和最长公共后缀,若最长公共前缀加最长公共后缀大于当前的i,则说明这里可以构成一个长度为i的重复串,那么就重新从1到n区模拟判断删去重复串,由于字符串缩减得很快,所以总复杂度为O( Nlog2N )。

代码

#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
const int maxn=50005,maxn1=10007;
int f[maxn],g[maxn],n,i,t,j,k,l,b[maxn],a[maxn],len,r,mid,bz1[maxn],x,y;
char s[maxn];
bool bz;
int pan(int t,int mid,int z){
    if (z) return f[t]-f[t-mid]*g[mid];
    return f[t+mid-1]-f[t-1]*g[mid];
}
int before(int t,int k){
    int l,r,mid;
    if (s[t]!=s[k]) return 0;
    l=1;
    r=min(t,k-t);
    while (l<r){
        mid=(l+r+1)/2;
        if (pan(t,mid,1)==pan(k,mid,1))l=mid;
        else r=mid-1;
    }
    return l;
}
int after(int t,int k){
    int l,r,mid;
    if (s[t]!=s[k]) return 0;
    l=1;
    r=min(k-t,n-k+1);
    while (l<r){
        mid=(l+r+1)/2;
        if (pan(t,mid,0)==pan(k,mid,0))l=mid;
        else r=mid-1;
    }
    return l;
}
int main(){
//  freopen("data.in","r",stdin);freopen("data.out","w",stdout);
    scanf("%s",s+1);
    n=strlen(s+1);g[0]=1;
    for (i=1;i<=n;i++){
        g[i]=g[i-1]*27;
        f[i]=f[i-1]*27+s[i]-96;
    }
    for (i=1;i<=n;i++){
        bz=true;
        len=0;
        for (j=2;j<=n/i;j++){
            t=i*(j-1);k=i*j;
            x=before(t,k);
            y=after(t,k);
            if (x+y>i){
                bz=false;break;
            }
        }
        if (bz) continue;
        l=1;r=i+1;
        while(r+i-1<=n){
            x=after(l,r);
            if (x>=i){
                for (k=l;k<r;k++)
                    bz1[k]=i;
                l+=i;r+=i;
            }else l++,r++;
        }
        for (j=1;j<=n;j++)
            if (bz1[j]!=i) s[++len]=s[j];
        n=len;
        for (j=1;j<=n;j++) f[j]=f[j-1]*27+s[j]-96;
    }
    for (i=1;i<=n;i++)
        printf("%c",s[i]);
    printf("\n");
}
  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值