字符串匹配问题
寻找长度为
n
n
n 的主串
S
S
S 中的匹配串
T
T
T(长
m
m
m)出现的位置或次数。
字符串哈希是将比较字符串转化为比较字符串的哈希值是否相等。
H
(
C
)
H(C)
H(C) 表示字符串
C
C
C 的哈希值,
H
(
C
,
k
)
H(C,k)
H(C,k) 表示字符串
C
C
C 到第
k
k
k 位的哈希值。
具体流程
取两个合适的互质常数
p
,
q
p,q
p,q,对于字符串
C
=
c
1
c
2
c
3
…
c
m
C=c_1c_2c_3\dots c_m
C=c1c2c3…cm
H
(
C
)
=
(
c
1
p
m
−
1
c
2
p
m
−
2
…
c
m
p
0
)
m
o
d
q
H(C)=(c_1p^{m-1}\;c_2p^{m-2}\;\dots c_mp^0) mod\; q
H(C)=(c1pm−1c2pm−2…cmp0)modq。一般可取
q
=
2
32
q=2^{32}
q=232 或
2
64
2^{64}
264 自然溢出。
此时比较
S
(
n
位
)
S(n位)
S(n位) 与
C
C
C 的子串
C
′
=
c
k
+
1
c
k
+
2
…
c
k
+
n
C'=c_{k+1}\;c_{k+2}\;\dots c_{k+n}
C′=ck+1ck+2…ck+n 是否匹配,只需比较
H
(
S
)
H(S)
H(S) 与
H
(
C
′
)
=
H
(
C
,
k
+
n
)
−
H
(
C
,
k
)
⋅
p
n
H(C')=H(C,k+n)-H(C,k)\cdot p^n
H(C′)=H(C,k+n)−H(C,k)⋅pn。
正确性
由于取模,不同字符串的哈希值可能相同,但若 q q q 较大,重复的概率较小。可使用双哈希,分别使用 1 0 9 + 7 和 1 0 9 + 9 10^9+7 和 10^9+9 109+7和109+9(孪生质数)。
题目
Oulipo
模板题
Code:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
using namespace std;
typedef unsigned long long ull;
string s1,s2;
ull pow[1000001];
ull has[1000001];
int main()
{
pow[0]=1;
for(int i=1;i<=1000000;i++)pow[i]=pow[i-1]*30;
int T;
cin>>T;
for(;T;T--)
{
int ans=0,len1,len2;
cin>>s1>>s2;
len1=s1.size();
len2=s2.size();
has[0]=0;
for(int i=0;i<len2;i++)
{
has[i+1]=has[i]*30+(ull)(s2[i]-'A'+1);
}
ull has1=0;
for(int i=0;i<len1;i++)
{
has1=has1*30+(ull)(s1[i]-'A'+1);
}
for(int i=0;i<=len2-len1;i++)
{
if(has1==has[i+len1]-has[i]*pow[len1])ans++/*,cout<<i<<"jjj"<<endl*/;
}
cout<<ans<<endl;
}
return 0;
}
Power String
此题似乎可以暴力过,暴力代码就不放了。
最简单的想法就是取前
1
1
1 位、
2
2
2 位枚举。
Code
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
typedef unsigned long long ull;
#define b 131
ull power[1000010];
ull has[1000010];
int main()
{
string s;
power[0]=1;
for(int i=1;i<=1000000;i++)power[i]=power[i-1]*b;
while(1)
{
cin>>s;
if(s==".")return 0;
int len=s.size();
has[0]=0;
for(int i=0;i<len;i++)has[i+1]=b*has[i]+(ull)(s[i]);
// for(int i=1;i<=len;i++)cout<<power[i]<<endl;
for(int i=1;i<=len;i++)
{
if(len%i!=0)continue;
bool flag=1;
for(int j=i;j<len;j+=i)
{
if(has[i]!=has[j+i]-has[j]*power[i])
{
flag=0;
// cout<<j<<"ooo"<<endl;
break;
}
}
if(flag)
{
cout<<len/i<<endl;
break;
}
}
}
return 0;
}
Seek the Name, Seek the Fame
还是枚举长度。
Code
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
typedef unsigned long long ull;
#define b 131
ull power[400010];
ull has[400010];
int main()
{
string s;
power[0]=1;
for(int i=1;i<=400000;i++)power[i]=power[i-1]*b;
while(cin>>s)
{
int len=s.size();
has[0]=0;
for(int i=0;i<len;i++)has[i+1]=b*has[i]+(ull)(s[i]);
for(int i=1;i<=len;i++)
{
if(has[i]==has[len]-has[len-i]*power[i])cout<<i<<" ";
}
cout<<endl;
}
return 0;
}
friends
由于 s s s 被复制过,所以 U U U 中一定含有一个完整的 s s s。枚举插入的位置。
Code
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
using namespace std;
typedef unsigned long long ull;
#define b 33
int n;
string s;
bool npr[500010];
int prime[42000],zxzys[500010],fac[30];
ull has[500010],power[500010];
void sprime()
{
int cnt=0;
for(int i=2;i<=n;i++)
{
if(!npr[i])prime[++cnt]=i,zxzys[i]=i;
for(int j=1;prime[j]*i<=n&&j<=cnt;j++)
{
npr[prime[j]*i]=1;
zxzys[prime[j]*i]=prime[j];
if(i%prime[j]==0)break;
}
}
}
bool judge(int l,int r,int len)
{
return has[r-len]-has[l-1]*power[r-l-len+1]==has[r]-has[l+len-1]*power[r-l-len+1];
}
int main()
{
cin>>n>>s;
sprime();
power[0]=1;
for(int i=1;i<=n;i++)power[i]=power[i-1]*b;
has[0]=0;
for(int i=1;i<=n;i++)has[i]=has[i-1]*b+(ull)(s[i-1]-'a'+1);
// for(int i=1;i<=n;i++)cout<<has[i]<<" "<<power[i]<<endl;
int q;
cin>>q;
for(;q;q--)
{
int l,r,cnt=0;
scanf("%d%d",&l,&r);
if(l==r)
{
puts("1");
continue;
}
int len=r-l+1;
while(len>1)
{
// cout<<zxzys[len]<<endl;
// break;
fac[++cnt]=zxzys[len];
len/=fac[cnt];
}
len=r-l+1;
for(int i=1;i<=cnt;i++)
{
if(judge(l,r,len/fac[i]))len/=fac[i];
}
printf("%d\n",len);
}
return 0;
}
P3498 [POI2010]KOR-Beads
枚举每个长度。判重可用 map
。(set
也行)
Code
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
#include<map>
using namespace std;
typedef unsigned long long ull;
#define b 1000000007
ull has1[200001],has2[200001],power[200001];
int s[200001],n;
int ansc=0,cnt=0,ans[200001],ansp=0;
map <ull,bool> mapp;
int main()
{
cin>>n;
for(int i=1;i<=n;i++)cin>>s[i];
power[0]=1;
for(int i=1;i<=n;i++)power[i]=power[i-1]*b,has1[i]=has1[i-1]*b+s[i];
for(int i=n;i>=1;i--)has2[i]=has2[i+1]*b+s[i];
for(int i=1;i<=n/2;i++)
{
mapp.clear();
cnt=0;
for(int j=i;j<=n;j+=i)
{
if(n/i<ansp)break;//小小的特判
ull h1=has1[j]-has1[j-i]*power[i],h2=has2[j-i+1]-has2[j+1]*power[i];
// cout<<h1<<" "<<h2<<"ooo"<<endl;
if(mapp[h1])continue;
cnt++;
// printf("(%d,%d)\n",j-i+1,j);
mapp[h1]=1;
mapp[h2]=1;
}
if(cnt>ansp)ansc=1,ans[ansc]=i,ansp=cnt;
else if(cnt==ansp)ansc++,ans[ansc]=i;
// cout<<i<<" "<<cnt<<endl;
}
if(ansp==1)
{
cout<<ansp<<" "<<ansc+n-n/2<<endl;
for(int i=1;i<=ansc;i++)cout<<ans[i]<<" ";
for(int i=n/2+1;i<=n;i++)cout<<i<<" ";
return 0;
}
cout<<ansp<<" "<<ansc<<endl;
for(int i=1;i<=ansc;i++)cout<<ans[i]<<" ";
}
[POI2010]ANT-Antisymmetry
暴力
单纯枚举子串中点,可以拿到 60 分的高分(LOJ 上竟有 82 分)。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
string s;
bool str[500001];
int n;
int main()
{
cin>>n>>s;
for(int i=1;i<=n;i++)
{
str[i]=s[i-1]=='1'?1:0;
}
long long ans=0;
for(int mid=1;mid<=n;mid++)
{
for(int i=0;mid>i&&mid+i+1<=n;i++)
{
if(!(str[mid-i]^str[mid+i+1]))break;
ans++;
}
}
cout<<ans<<endl;
return 0;
}
正解
正解当然是 hash(废话)+二分。
原串求哈希,然后取反并倒序再求一遍,枚举子串中点然后二分子串长度。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
typedef unsigned long long ull;
using namespace std;
string s;
int str[500001];
ull has1[500001],has2[500001],power[500001];
int n;
bool check(int mid,int len)
{
return has1[mid+len+1]-has1[mid-len-1]*power[len*2+2]==has2[mid-len]-has2[mid+len+2]*power[len*2+2];
}
int main()
{
cin>>n>>s;
for(int i=1;i<=n;i++)
{
str[i]=s[i-1]=='1'?1:0;
}
power[0]=1;
for(int i=1;i<=n;i++)has1[i]=has1[i-1]*3+str[i],power[i]=power[i-1]*3;
for(int i=n;i>=1;i--)has2[i]=has2[i+1]*3+1-str[i];
long long ans=0;
for(int mid=1;mid<=n;mid++)
{
int l=0,r=min(mid-1,n-mid-1),len=0;
if(r==l)
{
if(str[mid]^str[mid+1])ans++;
continue;
}
while(l<=r)
{
len=(l+r)/2;
if(check(mid,len))l=len+1;
else r=len-1;
}
ans+=l;
}
cout<<ans<<endl;
return 0;
}