题目链接:https://vjudge.net/problem/HDU-4333
题意:
给出一个数字字符串s,问这个字符串的循环同构串中有多少个小于s,多少个等于s,多少个大于s。相同的只算一次。
比如 s=“231”
循环同构串有 231,123,312.
有一个大于s,一个小于s,一个等于s。
题解:
扩展kmp。
可以发现这个字符串出现循环节才有重复。那么求出最小循环节,算出这个循环节重复了多少次,那么最后的答案除以这个数字。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=2e5+5;
int nex[maxn],extend[maxn];
char s[maxn];
//预处理计算Next数组
void getNext(char s[]){
int i=0,j,len=strlen(s);
nex[0]=len;
while(s[i]==s[i+1]&&i+1<len) ++i;
nex[1]=i;
int p=1;
for(i=2;i<len;i++){
if(nex[i-p]+i<nex[p]+p) nex[i]=nex[i-p];
else {
j=nex[p]+p-i;
if(j<0) j=0;
while(i+j<len&&s[j]==s[j+i]) j++;
nex[i]=j;
p=i;
}
}
}
//计算extend数组
void EXKMP(char* s1,char* s2){
int i=0,j,p,len=strlen(s1),len2=strlen(s2);
getNext(s2);
while(s1[i]==s2[i]&&i<len2&&i<len) i++;
extend[0]=i;
p=0;
for(i=1;i<len;i++){
if(nex[i-p]+i<extend[p]+p) extend[i]=nex[i-p];
else {
j=extend[p]+p-i;
if(j<0) j=0;
while(i+j<len&&j<len2&&s1[j+i]==s2[j]) j++; extend[i]=j;
p=i;
}
}
}
void getnext(char *s){
int len=strlen(s);
nex[0]=-1;
int k=-1,j=0;
while(j<len){
if(k==-1||s[k]==s[j]) nex[++j]=++k;
else k=nex[k];
}
}
int main(){
int t;
scanf("%d",&t);
for(int cas=1;cas<=t;cas++){
scanf("%s",s);
int n=strlen(s);
for(int i=0;i<n;i++) s[i+n]=s[i];
n*=2;
s[n]=0;
EXKMP(s,s+n/2);
int a=0,b=0,c=0;
for(int i=0;i<n/2;i++){
if(extend[i]>=n/2){
b++;
}else{
if(s[i+extend[i]]>s[extend[i]]) c++;
else a++;
}
}
getnext(s);
int t=1;
int x=n-nex[n];
if(n%x==0) t=n/2/x;
printf("Case %d: %d %d %d\n",cas,a/t,b/t,c/t);
}
return 0;
}
HDU6629也是扩展kmp的一道题。
HDU6629代码:https://paste.ubuntu.com/p/w7bFrXYZ9z/