传送门
题意:
求小串在大串中第一次的匹配位置。
题解:
简单的字符串匹配,KMP模板题。
code:
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int max_n=1e6+5;
const int max_m=1e4+5;
int w[max_n],s[max_m];
int T[max_m];
int t,n,m,ans;
inline void calc_T(){
memset(T,0,sizeof(T));
T[0]=-1;
int j;
for (int i=0;i<m;++i){
j=T[i];
while (j!=-1&&s[i]!=s[j])
j=T[j];
T[i+1]=++j;
}
}
inline void calc_ans(){
ans=-1;
int j=0;
for (int i=0;i<n;++i){
while (j!=-1&&w[i]!=s[j])
j=T[j];
++j;
if (j==m){
ans=(i+1)-m+1;
return;
}
}
}
int main(){
scanf("%d",&t);
while (t--){
scanf("%d%d",&n,&m);
for (int i=0;i<n;++i) scanf("%d",&w[i]);
for (int i=0;i<m;++i) scanf("%d",&s[i]);
calc_T();
calc_ans();
printf("%d\n",ans);
}
}
传送门
题意:
求字符串的每个前缀在字符串中出现的次数之和。
题解:
前缀会在后面字符的失配里出现,其实就是求每一个位置的失配个数之和。
注意这里判断条件是失配不为-1,因为如果失配等于0的话其实是代表了前缀自身的出现。
code:
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int max_s=2e5+5;
const int Mod=10007;
char s[max_s];
int t,len,ans;
int T[max_s];
inline void calc_T(){
memset(T,0,sizeof(T));
T[0]=-1;
int j;
for (int i=0;i<len;++i){
j=T[i];
while (j!=-1&&s[i]!=s[j])
j=T[j];
T[i+1]=++j;
}
}
inline void calc_ans(){
ans=0;
int j;
for (int i=1;i<=len;++i){
j=T[i];
while (j!=-1)
j=T[j],ans=(ans+1)%Mod;
}
}
int main(){
scanf("%d",&t);
while (t--){
scanf("%d\n",&len);
gets(s);
calc_T();
calc_ans();
printf("%d\n",ans);
}
}
传送门
题意:
求使给出字符串至少循环两次的最少的添加字符的个数。
题解:
最小循环节=字符串长度-末位失配。求出不足最小循环节的个数即为答案。
code:
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int max_s=1e5+5;
char s[max_s];
int t,len,ans;
int T[max_s];
inline void calc_T(){
T[0]=-1;
int j;
for (int i=0;i<len;++i){
j=T[i];
while (j!=-1&&s[i]!=s[j])
j=T[j];
T[i+1]=++j;
}
}
inline void calc_ans(){
if (len%(len-T[len])==0&&len!=len-T[len]) ans=0;
else ans=len-T[len]-(len%(len-T[len]));
}
int main(){
scanf("%d\n",&t);
while (t--){
gets(s);
len=strlen(s);
calc_T();
calc_ans();
printf("%d\n",ans);
}
}
传送门
题意:
求每一位的大于1的最大循环次数。
题解:
还是利用上面的结论:最小循环节=长度-末位失配。枚举每一位即可。
code:
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int max_n=1e6+5;
int n,num;
char s[max_n];
int T[max_n];
inline void calc_T(){
T[0]=-1;
int j;
for (int i=0;i<n;++i){
j=T[i];
while (j!=-1&&s[i]!=s[j])
j=T[j];
T[i+1]=++j;
}
}
int main(){
while (~scanf("%d\n",&n)){
if (!n) return 0;
memset(T,0,sizeof(T));
printf("Test case #%d\n",++num);
gets(s);
calc_T();
for (int i=2;i<=n;++i){
if (i%(i-T[i])==0&&i!=i-T[i])
printf("%d %d\n",i,i/(i-T[i]));
}
putchar('\n');
}
}
传送门
难得的中文题。。。
题意:
求小串在大串中最多不重复匹配次数。
题解:
KMP经典模型,每次找到之后不跳到失配,直接跳到0即可。
code:
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int max_s=1005;
char W[max_s],s[max_s];
int T[max_s];
int len_s,len_W,ans;
inline void calc_T(){
memset(T,0,sizeof(T));
T[0]=-1;
int j;
for (int i=0;i<len_s;++i){
j=T[i];
while (j!=-1&&s[i]!=s[j])
j=T[j];
T[i+1]=++j;
}
}
inline void calc_ans(){
int j=0;
ans=0;
for (int i=0;i<len_W;++i){
while (j!=-1&&W[i]!=s[j])
j=T[j];
++j;
if (j==len_s){
ans++;
j=0;
}
}
}
int main(){
while (~scanf("%s",W)){
if (W[0]=='#') return 0;
scanf("%s",s);
len_s=strlen(s);
len_W=strlen(W);
calc_T();
calc_ans();
printf("%d\n",ans);
}
}
传送门
题意:
求最长公共前缀后缀。
题解:
将两个串相接,然后求失配函数即可。注意判断末位失配与两个字符串长度的关系。
code:
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int max_s=5e5+5;
char s[max_s],s2[max_s];
int T[max_s];
int l1,l2,len,ans;
inline void calc_T(){
memset(T,0,sizeof(T));
T[0]=-1;
int j;
for (int i=0;i<len;++i){
j=T[i];
while (j!=-1&&s[i]!=s[j])
j=T[j];
T[i+1]=++j;
}
}
inline void calc_ans(){
if (T[len]<=min(l1,l2)) ans=T[len];
else ans=min(l1,l2);
}
int main(){
while (~scanf("%s %s",s,s2)){
l1=strlen(s);
l2=strlen(s2);
for (int i=0;i<l2;++i)
s[l1+i]=s2[i];
len=l1+l2;
calc_T();
calc_ans();
for (int i=0;i<ans;++i)
putchar(s[i]);
if (ans) putchar(' ');
printf("%d\n",ans);
}
}