觉得自己的kmp写的太丑了,但是又习惯了这么写了。。。
kmp匹配的模板题吧。
#include
using namespace std;
typedef long long LL;
const int maxn=1000005;
const int maxm=10005;
const int inf=0x3f3f3f3f;
int n,m;
int s[maxn],t[maxm];
int nxt[maxm];
void getnxt(){
memset(nxt,-1,sizeof(nxt));
for(int i=1;iint temp=nxt[i-1];
while(temp!=-1){
if(t[temp+1]==t[i]){nxt[i]=temp+1;break;}
temp=nxt[temp];
}
if(temp==-1){
if(t[i]==t[0]){nxt[i]=0;}
}
}
// for(int i=0;i
}
int f(){
int j=0;
for(int i=0;i<=n;i++){
while(j&&s[i]!=t[j]){
j=nxt[j-1]+1;
}
if(s[i]==t[j]){
j++;
}
if(j==m)return i-j+2;
}
return -1;
}
int main()
{
int T;
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&m);
for(int i=0;iscanf ("%d",&s[i]);
}
for(int i=0;iscanf("%d",&t[i]);
}
getnxt();
printf("%d\n",f());
}
return 0;
}
同模板题,不过要输出匹配上的次数。
#include
#include
using namespace std;
typedef long long LL;
const int maxn=1000000;
const int maxm=10005;
const int inf=0x3f3f3f3f;
int n,m;
char a[maxn],b[maxn];
int ans,alen,blen;
int nxt[maxn];
void getnxt(){
memset(nxt,-1,sizeof(nxt));
for(int i=1;iint temp=nxt[i-1];
while(temp!=-1){
if(b[i]==b[temp+1]){nxt[i]=temp+1;break;}
temp=nxt[temp];
}
if(temp==-1)if(b[i]==b[0])nxt[i]=0;
}
}
void f(){
int j=0;
for(int i=0;iwhile(j&&a[i]!=b[j])j=nxt[j-1]+1;
if(a[i]==b[j])j++;
if(j==blen){
ans++;
}
}
}
int main()
{
scanf("%d",&n);
while(n--){
scanf("%s%s",b,a);
ans=0;
alen=strlen(a);
blen=strlen(b);
getnxt();
f();
printf("%d\n",ans);
}
return 0;
}
还是字符串匹配,不过匹配出来的串不能互相重叠,所以每次匹配成功后要把j设为0.
#include
using namespace std;
typedef long long LL;
const int maxn=1005;
const int maxm=10005;
const int inf=0x3f3f3f3f;
int n,m;
char a[maxn],b[maxn];
int ans,alen,blen;
int nxt[maxn];
void getnxt(){
memset(nxt,-1,sizeof(nxt));
for(int i=1;iint temp=nxt[i-1];
while(temp!=-1){
if(b[i]==b[temp+1]){nxt[i]=temp+1;break;}
temp=nxt[temp];
}
if(temp==-1)if(b[i]==b[0])nxt[i]=0;
}
}
void f(){
int j=0;
for(int i=0;iwhile(j&&a[i]!=b[j])j=nxt[j-1]+1;
if(a[i]==b[j])j++;
if(j==blen){
ans++;
j=0;
}
}
}
int main()
{
while(scanf("%s",a)!=EOF){
if(a[0]=='#')break;
scanf("%s",b);
ans=0;
alen=strlen(a);
blen=strlen(b);
getnxt();
f();
printf("%d\n",ans);
}
return 0;
}
项链要求颜色排列为大于1的循环,让你往项链的左右添加珠子使得满足条件,输出最小添加数量。
需要利用next指针的性质找出字符串的循环节,如果可以被字符串长度整除的话就已经满足了,否则添加珠子使满足被整除。
#include
using namespace std;
typedef long long LL;
const int maxn=100005;
const int maxm=10005;
const int inf=0x3f3f3f3f;
int n,m;
char str[maxn];
int len;
int nxt[maxn];
void get_nxt(){
memset(nxt,-1,sizeof(nxt));
for(int i=1;iint temp=nxt[i-1];
while(temp!=-1){
if(str[i]==str[temp+1]){nxt[i]=temp+1;break;}
temp=nxt[temp];
}
if(temp==-1){
if(str[i]==str[0]){nxt[i]=0;}
}
}
}
int main()
{
int T;
scanf("%d",&T);
while(T--){
scanf("%s",str);
len=strlen(str);
get_nxt();
int ans;
int cnt=len-nxt[len-1]-1;
if(nxt[len-1]+1==0){ans=len;}
else if(len%cnt==0)ans=0;
else{
ans=len%cnt;
ans=cnt-ans;
}
printf("%d\n",ans);
}
return 0;
}
A^K表示K个字符串A连接起来,让你找出所有前缀可以那样表示且K>1的。同一个前缀只找到k最大的那个。
还是利用next指针的特性找可以前缀长度被整除的循环节即可。
#include
using namespace std;
typedef long long LL;
const int maxn=1000005;
const int maxm=10005;
const int inf=0x3f3f3f3f;
int n,m;
char str[maxn];
int len;
int nxt[maxn];
void get_nxt(){
memset(nxt,-1,sizeof(nxt));
for(int i=1;iint temp=nxt[i-1];
while(temp!=-1){
if(str[i]==str[temp+1]){nxt[i]=temp+1;break;}
temp=nxt[temp];
}
if(temp==-1){
if(str[i]==str[0]){nxt[i]=0;}
}
}
for(int i=0;iint main()
{
int cas=0;
while(~scanf("%d",&len),len){
cas++;
printf("Test case #%d\n",cas);
scanf("%s",str);
get_nxt();
for(int i=0;iif((i+1)%(i+1-nxt[i])==0){
if((i+1)/(i+1-nxt[i])>1)
printf("%d %d\n",i+1,(i+1)/(i+1-nxt[i]));
}
}
printf("\n");
}
return 0;
}
还是找最小循环节。
#include
using namespace std;
typedef long long LL;
const int maxn=1000005;
const int maxm=10005;
const int inf=0x3f3f3f3f;
int n,m;
char str[maxn];
int len;
int nxt[maxn];
void get_nxt(){
memset(nxt,-1,sizeof(nxt));
for(int i=1;iint temp=nxt[i-1];
while(temp!=-1){
if(str[i]==str[temp+1]){nxt[i]=temp+1;break;}
temp=nxt[temp];
}
if(temp==-1){
if(str[i]==str[0]){nxt[i]=0;}
}
}
for(int i=0;iint main()
{
while(~scanf("%s",str)){
len=strlen(str);
get_nxt();
int ans=len-nxt[len-1];
printf("%d\n",ans);
}
return 0;
}
找最大的n使得存在a^n=s,
同样是找满足整除关系的最小循环节。
//#include
#include
#include
using namespace std;
typedef long long LL;
const int maxn=1000005;
const int maxm=10005;
const int inf=0x3f3f3f3f;
int n,m;
char str[maxn];
int len;
int nxt[maxn];
void get_nxt(){
memset(nxt,-1,sizeof(nxt));
for(int i=1;iint temp=nxt[i-1];
while(temp!=-1){
if(str[i]==str[temp+1]){nxt[i]=temp+1;break;}
temp=nxt[temp];
}
if(temp==-1){
if(str[i]==str[0]){nxt[i]=0;}
}
}
for(int i=0;iint main()
{
while(~scanf("%s",str),str[0]!='.'){
len=strlen(str);
get_nxt();
int ans;
int cnt=len-nxt[len-1];
if(cnt==0)ans=1;
else {
if(len%cnt==0)
ans=len/cnt;
else ans=1;
}
printf("%d\n",ans);
}
return 0;
}
找同时是字符串前缀和后缀的子串。
沿着最后一个字母的next字母往回走的那些就是满足条件的了
//#include
#include
#include
#include
#include
using namespace std;
typedef long long LL;
const int maxn=1000005;
const int maxm=10005;
const int inf=0x3f3f3f3f;
int n,m;
char str[maxn];
int len;
int nxt[maxn];
vector<int> ans;
void get_nxt(){
memset(nxt,-1,sizeof(nxt));
for(int i=1;iint temp=nxt[i-1];
while(temp!=-1){
if(str[i]==str[temp+1]){nxt[i]=temp+1;break;}
temp=nxt[temp];
}
if(temp==-1){
if(str[i]==str[0]){nxt[i]=0;}
}
}
}
int main()
{
while(~scanf("%s",str)){
ans.clear();
len=strlen(str);
int len1=len;
get_nxt();
ans.push_back(len);
len--;
while(len>0){
len=nxt[len];
if(len<=0)break;
ans.push_back(len+1);
}
if(str[0]==str[len1-1]&&len1!=1)ans.push_back(1);
sort(ans.begin(),ans.end());
int sss=ans.size();
for(int i=0;iprintf("%d",ans[i]);
if(i1)printf(" ");
}
puts("");
}
return 0;
}
多字符串的最长公共子串。
数据量小,暴力匹配了。。。
//#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long LL;
const int maxn=1000005;
const int maxm=10005;
const int inf=0x3f3f3f3f;
int n,m;
string str[15];
int len;
int nxt[maxn];
void get_nxt(){
memset(nxt,-1,sizeof(nxt));
for(int i=1;iint temp=nxt[i-1];
while(temp!=-1){
if(str[i]==str[temp+1]){nxt[i]=temp+1;break;}
temp=nxt[temp];
}
if(temp==-1){
if(str[i]==str[0]){nxt[i]=0;}
}
}
}
int main()
{
int T;
scanf("%d",&T);
while(T--){
scanf("%d",&n);
for(int i=0;icin>>str[i];
}
len=str[0].length();
string ans;
bool f=0;
for(int i=len;i>=3;i--){
for(int j=0;j+i-1string cnt=str[0].substr(j,i);
bool f1=1;
for(int k=1;kint cur=str[k].find(cnt);
if(cur==-1)f1=0;
}
if(f1){
if(cnt"")
ans=cnt;
f=1;
}
}
if(f)break;
}
if(f==0)printf("no significant commonalities\n");
else{cout<return 0;
}
找a串的前缀和b串的后缀最长的相等部分。
a串求next指针去匹配b串,匹配到结尾时a串的匹配长度就是答案。
我的匹配的部分似乎有点问题???
//#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long LL;
const int maxn=50005;
const int maxm=10005;
const int inf=0x3f3f3f3f;
int n,m;
char a[maxn],b[maxn];
int nxt[maxn];
int lena,lenb;
void get_nxt(){
memset(nxt,-1,sizeof(nxt));
for(int i=1;iint temp=nxt[i-1];
while(temp!=-1){
if(a[i]==a[temp+1]){nxt[i]=temp+1;break;}
temp=nxt[temp];
}
if(temp==-1){
if(a[0]==a[i])nxt[i]=0;
}
}
for(int i=0;iint kmp(){
int j=0;
for(int i=0;iwhile(j!=0&&a[j]!=b[i]){
j=nxt[j-1];
}
if(a[j]==b[i]){j++;}
if(j==lena&&i!=lenb-1)j=nxt[j-1];
}
return j;
}
int main()
{
while(~scanf("%s%s",a,b)){
lena=strlen(a);
lenb=strlen(b);
get_nxt();
int ans=kmp();
for(int i=0;iputchar(a[i]);
if(ans)putchar(' ');
printf("%d\n",ans);
}
return 0;
}
求所有前缀在字符串中匹配成功次数之和。
还是利用next求最长公共前后缀的性质,从每个位置沿着next指针往回不断对答案+1就行了。
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define MEM(a,b) memset(a,b,sizeof(a));
typedef long long ll;
const int maxn=200005;
const int maxm=1005;
const int inf=0x3f3f3f3f;
const int mod=10007;
int n,m;
char s[maxn];
int nxt[maxn];
int num[maxn];//当前这个前缀的相同串有几个
void get_nxt(){
memset(nxt,-1,sizeof(nxt));
for(int i=1;iint tmp=nxt[i-1];
while(tmp!=-1){
if(s[i]==s[tmp+1]){nxt[i]=tmp+1;break;}
tmp=nxt[tmp];
}
if(tmp==-1){
if(s[i]==s[0])nxt[i]=0;
}
}
}
int main()
{
int T;
scanf("%d",&T);
while(T--){
scanf("%d",&n);
scanf("%s",s);
get_nxt();
int ans=0;
for(int i=n-1;i>=0;i--){
int j=i;
while(j!=-1){
ans=(ans+1)%mod;
j=nxt[j];
}
}
printf("%d\n",ans);
}
return 0;
}
一串密文与明文前面一部分(可能完整)连接起来的文本,让你输出完整的最短密文明文。
明文那部分翻译成密文,然后求next指针,就能的到最长的公共前后缀,那个就是明文的长度了。
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define MEM(a,b) memset(a,b,sizeof(a));
typedef long long ll;
const int maxn=100005;
const int maxm=1005;
const int inf=0x3f3f3f3f;
const int mod=10007;
int n,m;
char ff[30],ff1[30];
char s[maxn],t[maxn];
int nxt[maxn];
void get_nxt(){
memset(nxt,-1,sizeof(nxt));
for(int i=(n-1)/2+1;iint tmp=nxt[i-1];
while(tmp!=-1){
if(s[i]==s[tmp+1]){nxt[i]=tmp+1;break;}
tmp=nxt[tmp];
}
if(tmp==-1){
if(s[i]==s[0])nxt[i]=0;
}
}
}
int main()
{
int T;
scanf("%d",&T);
while(T--){
scanf("%s%s",ff,t);
n=strlen(t);
for(int i=0;i<26;i++){
ff1[ff[i]-'a']=i+'a';
}
for(int i=0;ifor(int i=(n-1)/2+1;i'a'];
}
get_nxt();
int ans=nxt[n-1]+1;
ans=n-ans;
for(int i=0;iputchar(t[i]);
}
for(int i=0;iputchar(ff1[t[i]-'a']);
}
puts("");
}
return 0;
}
还是暴力匹配了,数据应该不科学
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define MEM(a,b) memset(a,b,sizeof(a));
typedef long long ll;
const int maxn=100005;
const int maxm=1005;
const int inf=0x3f3f3f3f;
const int mod=10007;
int n,m;
string str[105];
int main()
{
int T;
scanf("%d",&T);
while(T--){
scanf("%d",&n);
int id=0;
for(int i=0;icin>>str[i];
if(str[i].length()int ans=0;
int len=str[id].length();
for(int lll=len;lll>0;lll--){
for(int i=0;i+lll-1string cnt=str[id].substr(i,lll);
string cnt1=cnt;
reverse(cnt1.begin(),cnt1.end());
bool f=1;
for(int j=0;jif(j==id)continue;
if(str[j].find(cnt)==string::npos&&str[j].find(cnt1)==string::npos){f=0;break;}
}
if(f){ans=lll;break;}
}
if(ans)break;
}
printf("%d\n",ans);
}
return 0;
}
找字符串前缀中间后缀三段相同不重叠的最长子串。
我这个参考别人写的二分应该是错的,虽然过了。
正解应该是找最长公共前后缀,然后kmp中间部分看能不能匹配上,不能的话next指针再往前走一步,直到能匹配上,这样才能保证前后缀始终相等。
#include
#include
#include
#include
#include
#include
using namespace std;
#define MEM(a,b) memset(a,b,sizeof(a));
typedef long long ll;
const int maxn=1000005;
const int maxm=3000005;
const int inf=0x3f3f3f3f;
int n,len;
int nxt[maxn];
char str[maxn];
void get_nxt(char *s){
memset(nxt,-1,sizeof(nxt));
for(int i=1;iint tmp=nxt[i-1];
while(tmp!=-1){
if(s[i]==s[tmp+1]){nxt[i]=tmp+1;break;}
tmp=nxt[tmp];
}
if(tmp==-1){
if(s[i]==s[0])nxt[i]=0;
}
}
}
bool kmp(int k){
int i=k-1,j=0;
while(iif(j==-1||str[i]==str[j]){i++;j++;}
else j=nxt[j];
}
return j>=k;
}
int main()
{
int T;
scanf("%d",&T);
while(T--){
scanf("%s",str);
n=strlen(str);
get_nxt(str);
int hh=min(n/3,nxt[n-1]+1);
int lef=0,rig=hh;
int ans=0;
while(lef<=rig){
int mid=(lef+rig)/2;
if(kmp(mid)){ans=mid;lef=mid+1;}
else rig=mid-1;
}
printf("%d\n",ans);
}
return 0;
}