HIT暑期集训 字符串
一个网上找到关于马拉车算法的博客
https://www.jianshu.com/p/392172762e55
做法是枚举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; }
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; }
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; }