夏令营8.6——8.8字符串

T1 POJ3461

kmp裸题:
kmp资料可以参考http://www.tuicool.com/articles/e2Qbyyf(感谢)

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
using namespace std;
int next[10001];
int main()
{
    char s[1000001],t[100001];
    int n;
    cin>>n;
    for(int i=0;i<n;i++)
    {
        int k=0,ans=0;
        scanf("%s%s",&t,&s);
        int sl=strlen(s);
        int tl=strlen(t);  
        memset(next,0,sizeof(next));
        next[0]=0;
        for(int i=1;i<tl;i++)
        {
            while(k>0&&(t[i]!=t[k]))
            k=next[k-1];
            if(t[i]==t[k])
            k++;
            next[i]=k;
        }
        k=0;
        for(int i=0;i<sl;i++)
        {
            while(k>0&&s[i]!=t[k])
            k=next[k-1];
            if(s[i]==t[k])
            k++;
            if(k==tl)
            {
                k=next[k-1];
                ans++;
            }
        }
        cout<<ans<<endl;
    }
    return 0;
}

T2,manacher HDU3068

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int ans[220005];
int main()
{
    //freopen("t2.in","r",stdin);
    //freopen("t2.out","w",stdout);
    char ch[110001],s[220005];
    while(scanf("%s",ch)!=EOF)
    {
        memset(ans,0,sizeof(ans));
        memset(s,0,sizeof(s));
        int cl=strlen(ch),max1=0;
        s[0]='@';
        s[1]='#';
        for(int i=0,j=2;i<cl;i++,j+=2)
        {
            s[j]=ch[i];
            s[j+1]='#';
        }
        s[(cl+1)*2]='$';
        int sl=strlen(s);
        int p=1;
        for(int i=2;i<sl;i++)
        {
            ans[i]=max(0,min(ans[2*p-i],p+ans[p]-i));
            while(s[i-ans[i]-1]==s[i+ans[i]+1])
            ans[i]++;
            if(i+ans[i]>p+ans[p])
            p=i;
            if(ans[i]>max1)
            max1=ans[i];
        }
        printf("%d\n",max1);  
    }
    return 0;
}

T3, POJ2185 网上最常见的就是每一行的最短循环节的最小公倍数*每一列的最短循环节的最小公倍数。但是,实际上是不行的。

如: ABCDEFAB就不行,详情请参考http://blog.sina.com.cn/s/blog_69c3f0410100tyjl.html

         AAAABAAA

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
char s[1001][80];
int f[1001];
int next[1001];
int main()
{
    int n,m;
    cin>>n>>m;
    for(int i=0;i<n;i++)
    {
        scanf("%s",s[i]);
        for(int j=0;j<m;j++)
        {
            bool pd=true;
            for(int l=0;l<j+1;l++)
            {
            for(int k=1;k<=m/(j+1);k++)
            {
                if(l+k*(j+1)>=m)
                break;
                if(s[i][l]!=s[i][l+k*(j+1)])
                {
                    pd=false;
                    break;
                }
            }
            if(pd==false)
            break;
            }
            if(pd==true)
            f[j+1]++;
        }
    }
    int width;
    for(int i=1;i<=m;i++)
    if(f[i]==n)
    {
        width=i;
        break;
    }
    next[0]=0; int k=0;
    for(int i=1;i<n;i++)
    {
        while(k>0&&strcmp(s[i],s[k])) k=next[k-1];
        if(!strcmp(s[i],s[k])) k++;
        next[i]=k;
    }
    printf("%d\n",(n-next[n-1])*width);
    return 0;
}

T4.HDU4333 这道题目最好的方法是用拓展kmp,经过观察,其实就是求每一位的后缀与自身的最长公共前缀,这个显然可以用扩展kmp处理,但是开始的时候我超时了,后来在网上看到别人把这个串后面再接上本身,以它为主串,然后以自身为模板串进行扩展kmp,这样处理把时间复杂度降到了线性的,最后要注意的一个问题就是如何避免重复了,我们可以用普通的kmp求出此串的最小循环节,如果构成完整的循环,那么我们要算的就是循环节长度的情况了。

但是,我用了后缀数组的倍增法。按理来说应该也可以,但不知为什么RE了

#include<cstdio>
#include<iostream>
#include<cstring>
#include<ctime>
using namespace std;
const int N = int(400005);
int cmp(int *r,int a,int b,int l,int g){
    return (r[a]==r[b]) && (r[(a+l)%g]==r[(b+l)%g]);
}
int y[N],sa[N];
bool bo[400005];
int wa[N],wb[N],ws1[N],wv[N];
void da(char *r,int sa[],int n,int m,int t1){
    memset(ws1,0,sizeof(ws1));
    int i,j,p=0,*x=wa,*y=wb,*t;
    for(i=0;i<m;i++)
    ws1[i]=0;
    for(i=0;i<n;i++)
    {
        ws1[x[i]=r[i]]++;
    }
    for(i=1;i<m;i++)
    ws1[i]+=ws1[i-1];
    for(i=n-1;i>=0;i--)
    sa[--ws1[x[i]]]=i; //预处理长度为1
    for(j=1;j<n*2,p<n;j*=2,m=p) //通过已经求出的长度J的SA,来求2*J的SA
    {
        if(j>=n*2||p>=n)
        break;
        p=0;
        for(i=0;i<n;i++)
        {
            y[p++]=sa[i]-j;
            while(y[p-1]<0)
            y[p-1]+=n;
            while(y[p-1]>n)
            y[p-1]-=n;//利用长度J的,按第二关键字排序         
        }
        for(i=0;i<n;i++)
        wv[i]=x[y[i]];
        for(i=0;i<m;i++)
        ws1[i]=0;
        for(i=0;i<n;i++)
        ws1[wv[i]]++;
        for(i=1;i<=m;i++)
        ws1[i]+=ws1[i-1];
        for(i=n-1;i>=0;i--)
        sa[--ws1[wv[i]]]=y[i];
        t=x;
        x=y;
        y=t;
        x[sa[0]]=1;
        for(p=1,i=1;i<n;i++)
        {
        x[sa[i]]=cmp(y,sa[i-1],sa[i],j,n)?p:++p;                  
        }
    }
    int bz=x[0];
    int big=0,small=0,same=0;
    for(int i=0;i<n;i++)
    {
           if(x[i]>bz&&!bo[x[i]])
           {
                  big++;
               bo[x[i]]=true;               
           }
           if(x[i]<bz&&!bo[x[i]])
           {
               small++;
            bo[x[i]]=true;    
           }
           if(x[i]<bz&&!bo[x[i]])
           {
               same++;
            bo[x[i]]=true;    
           }
    }
    printf("%s %d%c %d %d %d\n","Case",t1,':',small,same,big);           
}
char str[N];
int main(){
//    freopen("t4.in","r",stdin);
//    freopen("t4.out","w",stdout);
    int t1;
    char str[N];
    scanf("%d",&t1);
    for(int i=1;i<=t1;i++)
    {
        memset(bo,false,sizeof(bo));
        int te=i;
        scanf("%s",str);
        int n = strlen(str);
        str[n]=0;
        da(str,sa,n,76,te);
    }
    //cout<<clock();
    return 0;
}

T5,hdu3613,manacher/EKMP

manacher,求出简单的回文串的变形,先将到每个位置的价值预处理出来然后将字符串跑一边马拉车,我枚举切每个位置的价值和,如现在枚举的是切第三个的位置,则判断一下前三个位置能否形成回文串,那么我们只用判断第二个位置的len1如果等于三则说明是回文串就醒了。

EKMP,将s1翻转得到s2,如果s1[i]---s1[len-1]=s2[0]---s[(len-1)-i],那么,这一段就是回文串,反过来也是一样。这可以用EKMP实现。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<queue>
#include<ctime>
#include<algorithm>
#include<map>
#include<iomanip>
#define INF 99999999
using namespace std;
const int MAX=500000+10;
char s1[MAX],s2[MAX];
int next1[MAX],extend1[MAX],extend2[MAX];
int sum[MAX],val[27],j;
void get_next(char *a,int len){
    int k=0,i=1;
    next1[0]=len;
    while(k+1<len&&a[k]==a[k+1]) k++;
    next1[1]=k;
    k=1;
    for(int i=2;i<len;i++)
    {
        int u=k+next1[k];
        if(u>i+next1[i-k]) next1[i]=next1[i-k];
        else
        {
            for(j=max(0,u-i);a[i]==a[i+j];j++);
            next1[i]=j;
            k=i;
        }
    }
}
void EKMP(char *a,char *b,int *extend,int len){
    get_next(a,len);
    int k=0,i=0;
    while(k<len && a[k] == b[k])++k;
    extend[0]=k;
    k=0;
    while(++i<len){
    int maxr=k+extend[k]-1;
    extend[i]=min(next1[i-k],max(maxr-i+1,0));
    while(i+extend[i]<len && a[extend[i]] == b[i+extend[i]])++extend[i];
    if(i+extend[i]>k+extend[k])k=i;
    }
}
int main()
{
    freopen("t5.in","r",stdin);
    freopen("t5.out","w",stdout);
    int t;
    cin>>t;
    for(int i=0;i<t;i++)
    {
        memset(sum,0,sizeof(sum));
        memset(next1,0,sizeof(next1));
        memset(extend1,0,sizeof(extend1));
        memset(extend2,0,sizeof(extend2));
        for(int i=0;i<26;i++)
        {
            scanf("%d",&val[i]);
        }
        scanf("%s",&s1);
        int len=strlen(s1);
        for(int i=1;i<=len;++i)
        {
            sum[i]=sum[i-1]+val[s1[i-1]-'a'];
            s2[i-1]=s1[len-i];
        }        
        EKMP(s1,s2,extend1,len);
        EKMP(s2,s1,extend2,len);
        int ans=0,temp=0;
        for(int i=1;i<len;++i){
            if(extend1[len-i] == i)temp+=sum[i];//表示前i个字符是
            if(extend2[i] == len-i)temp+=sum[len]-sum[i];//表示后len-i个字符是
            if(temp>ans)ans=temp;
            temp=0;
        }
        cout<<ans<<endl;
    }
    cout<<clock();
    return 0;
}

T6,水题,跳过。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值