CF 119D String Transformation(KMP,哈希,枚举,各种优化)

转载请注明出处,谢谢http://blog.csdn.net/ACM_cxlove?viewmode=contents    by---cxlove  

题目:有两个串,s,f  f(s, i, j) = s[i + 1... j - 1] + r(s[j... n - 1]) + r(s[0... i])

通过二元组(i,j)对原串s进行变换,得到新的串,现在要你求这个二元组

http://codeforces.com/problemset/problem/119/D 

哈哈,爽,终于A了

第一次在CF上调试这么艰辛,TLE+WA刷了好多

我们将两个串表示成a,b,分为三个部分

a  (  1  ,  2  ,  3  )     a'表示a的反序

那么b ( 2  ,  3'  ,   1' )    b'表示b的反序

我的做法是,第一步,枚举i,首先我们可以看到a和b‘的最长公共前缀。

由于 题目要求i最大,所以先求出a和b'的最长公共前缀长度,mx+1

然后i便可以从mx可能枚举。

接下来相当于枚举j,但是直接枚举肯定会TLE

可以发现b的后缀除了1'之后,便是a’的前缀。

那么我们用a'和b做一次KMP,记录对于b中的某个位置,能匹配的最远距离pos[]

但是并不一定j越远就好,所以枚举的时候,从pos[]出发,通过next[]数组就能枚举到可能的j

这样枚举出来的,肯定是满足1,3这两个部分

剩下的就是判断两个串的剩下部分也就是部分2是否匹配了。这一部分通过HASH就能O(1)处理。

但是这样还是会TLE,因为可以知道像aaaaaaaaaaabaaaaaaaaa这种串,通过next数组转移很慢的。还是相当于枚举

这里我又加了个优化,用b串和a串直接跑一次KMP,记录对于a中的某个位置,和b的前缀最长能匹配的长度dp[]。

那么在枚举i之后,枚举j,通过这个长度 能作一次剪枝

但是这里又WA了很久。这是因为在处理dp[]数组的时候,我只是在向前移动匹配的时候,转移了dp[]数组,其实是不对的。即使不能匹配,通过next[]数组,也是能得到后缀的前缀和前缀是匹配的,也是可以转移的。

以上想法,纯属是自己不成熟的做法,而且也表述不清

欢迎交流。可以看下代码

#include<iostream>  
#include<cstdio>  
#include<map>  
#include<cstring>  
#include<cmath>  
#include<vector>  
#include<algorithm>  
#include<set>  
#include<string>  
#include<queue>  
#define inf 1600005  
#define M 40  
#define N 1000005
#define maxn 300005  
#define eps 1e-12
#define zero(a) fabs(a)<eps  
#define Min(a,b) ((a)<(b)?(a):(b))  
#define Max(a,b) ((a)>(b)?(a):(b))  
#define pb(a) push_back(a)  
#define mp(a,b) make_pair(a,b)  
#define mem(a,b) memset(a,b,sizeof(a))  
#define LL long long  
#define MOD 1000000007
#define lson step<<1
#define rson step<<1|1
#define sqr(a) ((a)*(a))  
#define Key_value ch[ch[root][1]][0]  
#define test puts("OK");  
#define pi acos(-1.0)
#define lowbit(x) ((-(x))&(x))
#define HASH1 1331
#define HASH2 10001
#pragma comment(linker, "/STACK:1024000000,1024000000")  
using namespace std;
char a[N],a_2[N],b[N];
int la,lb;
int next[N];
int pos[N];
int dp[N];
LL fac1[N]={1},fac2[N]={1};
LL h11[N]={0},h21[N]={0};
LL h12[N]={0},h22[N]={0};
void get_next(char *s,int len){
    next[0]=-1;
    int i=0,j=-1;
    while(i<len){
        if(j==-1||s[i]==s[j]){
            i++;j++;
            next[i]=j;
        }
        else j=next[j];
    }
}
void match(char *pat,int lp,char *str,int ls,int flag){
    int i=0,j=0;
    while(i<lp&&j<ls){
        if(i==-1||pat[i]==str[j]){
            if(!flag) pos[j]=i;
            if(flag&&i!=-1){
                dp[j-i]=max(dp[j-i],i+1);
            }
            i++;j++;
        }
        else{
            i=next[i];
            dp[j-i]=max(dp[j-i],i+1);
        }
        if(i==lp) i=next[i];
    }
}
LL get(int l,int r,LL *hash,LL *fac){
    l++;r++;
    return hash[r]-hash[l-1]*fac[r-l+1];
}
int main(){
    //freopen("input.txt","r",stdin);
    for(int i=1;i<N;i++){
        fac1[i]=fac1[i-1]*HASH1;
        fac2[i]=fac2[i-1]*HASH2;
    }
    while(gets(a)!=NULL&&gets(b)!=NULL){
        la=strlen(a);lb=strlen(b);
        if(la!=lb){
            //if(a[0]==a[1]&&a[2]==a[3]&&a[2]==' ') cout<<3333333333<<endl;
            printf("-1 -1\n");
            continue;
        }
        for(int i=0;i<la;i++)
            a_2[i]=a[la-i-1];
        for(int i=1;i<=la;i++){
            h11[i]=h11[i-1]*HASH1+a[i-1];
            h12[i]=h12[i-1]*HASH2+a[i-1];
        }
        for(int i=1;i<=lb;i++){
            h21[i]=h21[i-1]*HASH1+b[i-1];
            h22[i]=h22[i-1]*HASH2+b[i-1];
        }
        get_next(a_2,la);
        match(a_2,la,b,lb,0);
        mem(dp,-1);
        get_next(b,lb);
        match(b,lb,a,la,1);
        int l=-1,r=-1,mx=-1;
        for(int i=0;i<la;i++)
            if(a[i]==b[lb-i-1]){
                mx=i;
            }
            else break;
        for(int i=min(la-2,mx);i>=0;i--){
            int p=pos[lb-i-2];

            while(p>=0){
                int j=la-p-1;
                if(j-i-1>dp[i+1]&&j-i-1!=0) break;
                if(i+1==j){
                    l=i,r=j;
                }
                else{
                    LL t1=get(i+1,j-1,h11,fac1),t2=get(0,j-i-2,h21,fac1);
                    LL t3=get(i+1,j-1,h12,fac2),t4=get(0,j-i-2,h22,fac2);
                    if(t1==t2&&t3==t4) l=i,r=j;
                }
                p=next[p];
                if(l!=-1) break;
            }
            if(l!=-1) break;
        }
        printf("%d %d\n",l,r);
    }
    return 0;
}






  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值