HIT暑期集训 字符串

一个网上找到关于马拉车算法的博客

https://www.jianshu.com/p/392172762e55

B    CodeForces 1056E

做法是枚举0串代表的字符串的长度,由这个长度可以推出1串代表的字符串长度,接着按照给出的01串的顺序检查每个0串,每个1串对应的字符串是否都分别相等(用hash)。若检查无误则答案加一。

做法复杂度很迷,并且hash也很迷,或许写双哈希会好一点,但是我太懒了就。。。

#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxn 1000005
#define mod 10000007
#define P 131
using namespace std;
typedef long long ll;
int lena,lenb,ans,cnt[2];
ll pw[maxn],f[maxn],ha[2];
char a[maxn],b[maxn];
void prework()
{
    int i;
    f[0]=0;pw[0]=1;
    for (i=1;i<=lenb;i++)
    {
        f[i]=(f[i-1]*P+(b[i]-'a'+1))%mod;
        pw[i]=pw[i-1]*P%mod;
    }
}
ll Hash(int l,int r)
{
    return (f[r]-f[l-1]*pw[r-(l-1)]%mod+mod)%mod;//我没学过的hash方法。。 
}
int main()
{
    int i,l,r,num0,num1,l0,l1,flag;
    ll tmp;
    scanf("%s%s",a+1,b+1);
    lena=strlen(a+1);
    lenb=strlen(b+1);
    for (i=1;i<=lena;i++) cnt[a[i]-'0']++;
    prework();
    for (l0=1;l0<lenb;l0++)//枚举0串长度
    {
        if (l0*cnt[0]>=lenb) break;//所有0串长度之和大于等于b串,退出 
        if ((lenb-l0*cnt[0])%cnt[1]!=0) continue;
        l1=(lenb-l0*cnt[0])/cnt[1];//1串长度 
        flag=1;
        ha[0]=-1;ha[1]=-2;
        num0=0;num1=0;//已经检查过的0/1数
        
        for (i=1;i<=lena;i++)
        {
            if (a[i]=='0')
            {
                l=num0*l0+num1*l1+1;//当前串的开始位置
                r=l+l0-1;//结束位置
                num0++;
                tmp=Hash(l,r);
                if (ha[0]==-1) ha[0]=tmp;
                else if (ha[0]!=tmp)
                {
                    flag=0;break;
                }
            }
            else if (a[i]=='1')
            {
                l=num0*l0+num1*l1+1;//当前串的开始位置
                r=l+l1-1;//结束位置
                num1++;
                tmp=Hash(l,r);
                if (ha[1]==-2) ha[1]=tmp;
                else if (ha[1]!=tmp)
                {
                    flag=0;break;
                } 
            }
            if (ha[0]==ha[1])
            {
                flag=0;break;
            }
        }
        if (flag && ha[0]!=ha[1]) ans++;
    }
    printf("%d",ans);
    return 0;
}
View Code

C    HDU 4300

把给出的密文+明文的a串全当作密文,翻译成明文得到新串b。可以发现a后面的明文部分会与b的前面由密文翻译而来的明文部分有公共前缀。所以将ab进行匹配,匹配出的字符串在a串中的起点位置之前的就是密文。注意匹配时要从a串中间开始匹配。

#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxn 100100
using namespace std;
int p[maxn];
char a[maxn],b[maxn],c[maxn],d[maxn];
void prework(int len)
{
    p[0]=-1;
    int i,j=-1;
    for (i=1;i<len;i++)
    {
        while (j>=0 && b[j+1]!=b[i]) j=p[j];
        if (b[j+1]==b[i]) j++;
        p[i]=j;
    }
} 
int work(int lim,int len)
{
    p[0]=-1;
    int i,j=-1;
    for (i=lim;i<len;i++)
    {
        while (j>=0 && b[j+1]!=a[i]) j=p[j];
        if (b[j+1]==a[i]) j++;
    }
    return j;
}
int main()
{
    int t,i,j,k,len,lc,lim,re;
    scanf("%d",&t);
    while (t--)
    {
        memset(p,0,sizeof(p));
        scanf("%s%s",c,a);
        lc=strlen(c);
        len=strlen(a);
        for (i=0;i<lc;i++) d[c[i]-'a']=i+'a';
        for (i=0;i<len;i++) b[i]=d[a[i]-'a'];
        prework(len);
        lim=(len+1)/2;
        re=work(lim,len);
        for (i=0;i<len-re-1;i++) printf("%c",a[i]);
        for (i=0;i<len-re-1;i++) printf("%c",b[i]);
        printf("\n");
    }
    return 0;
}
View Code

D    HDU 4821

E    HDU 5340

跑一遍马拉车,枚举第一个和第三个回文串,检查中间剩下的是不是回文串。

#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxn 40005
#define inf 99999999;
using namespace std;
int p[maxn],len,cnt1,cnt2,q1[maxn],q2[maxn];
char a[maxn],s[maxn];
void manacher()
{
    int mx=0,id,i;
    for (i=1;i<len;i++)
    {
        if (mx>i) p[i]=min(p[2*id-i],mx-i);
        else p[i]=1;
        while (s[i+p[i]]==s[i-p[i]]) p[i]++;
        if (i+p[i]>mx)
        {
            id=i;
            mx=i+p[i];
        }
    }
}
int main()
{
    int t,i,j,x,y,ans,mid;
    scanf("%d",&t);
    while (t--)
    {
        scanf("%s",a);
        cnt1=0;cnt2=0;ans=0;
        memset(p,0,sizeof(p));
        len=strlen(a);
        for (i=0;i<len;i++) 
        {
            s[i*2+1]=a[i];
            s[i*2+2]='#';
        }
        len=len*2;
        s[0]='$';
        s[++len]='@';
        manacher();
        for (i=1;i<len-1;i++)
        {
            if (i-p[i]==0) q1[++cnt1]=i+p[i];
            if (i+p[i]==len) q2[++cnt2]=i-p[i];
        }
        for (i=1;i<=cnt1;i++)
        for (j=1;j<=cnt2;j++)
        {
            x=q1[i];y=q2[j];
            if (x>=y) continue;
            mid=(x+y+1)/2;
            if (mid-p[mid]<=x && mid+p[mid]>=y)
            {
                ans=1;break;
            }
        }
        if (ans) printf("Yes\n");
        else printf("No\n");
    }
    return 0;
}
View Code

F    CodeForces 17E

posted @ 2020-08-08 11:44  lsy_kk  阅读(130)  评论(0编辑  收藏  举报