题意:给定一个数字,每次可以把最后一位移到第一位,分别统计所有移位结果中比原数小,相等,大的数目,重复的数字只算一次。
分析:e-kmp,设原数字字符串为 b,长度为m,把原数字字符串复制两遍当作母串 a,原数字字符串当作模式串做e-kmp,则extend数组分两种情况:
①extend[i]==m,即以 a[i] 为开头的m个数字与 b 相等,即这个移位结果与原串相等;
②extend[i]<m,这种情况下的移位结果肯定和原串不等,因为前 extend[i] 个数字相等,所以只要比较一下第 extend[i]+1 个数字就i可以了;
综上差不多可以构建出思路了,不过题目要求重复出现的只算作一次,所以我们得用 kmp 算出 b 的最小循环节 k,若 b 是由若干个最小循环节 k 组成(末尾没有不完整的循环节),那这种字符串移位的所有结果只会出现 len(k) 个;若非这种情况,那么字符串的每一个移位结果都肯定不同。
代码:
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int N = 1e5*2+10;
char a[N],b[N];
int nxt[N],extend[N],nt[N];
int n,m;
int init(char s[],int len,int nxt[])
{
nxt[0]=-1;
int j=0,k=-1;
while(j<len)
{
if(k==-1||s[j]==s[k])
{
++j;++k; nxt[j]=k;
}
else
{
k=nxt[k];
}
}
int cnt=len-nxt[len];
if(len%cnt==0) return cnt; //若干个完整相同循环节组成
return len;
}
void GetNxt(char s[],int len,int nxt[])
{
int i=0,j,po;
nxt[0]=len;
while(s[i]==s[i+1]&&i+1<len) i++; nxt[1]=i; //计算nxt[1]
po=1; //初始化po的位置
for(i=2;i<len;i++)
{
if(nxt[i-po]+i<nxt[po]+po) nxt[i]=nxt[i-po]; //第一种情况,可以直接得到nxt[i]的值
else //第二种情况,要继续匹配才能得到nxt[i]的值
{
j=nxt[po]+po-i;
if(j<0) j=0; //如果i>po+nxt[po],则要从头开始匹配
while(i+j<len&&s[j]==s[j+i]) j++; nxt[i]=j;
po=i; //更新po的位置
}
}
}
//计算Extendend数组
void ExKMP(char a[],int lena,char b[],int lenb)
{
int i=0,j,po;
while(a[i]==b[i]&&i<lena&&i<lenb) i++; extend[0]=i;
po=0;
for(i=1;i<lena;i++)
{
if(nxt[i-po]+i<extend[po]+po) //第一种情况,直接可以得到Extendend[i]的值
extend[i]=nxt[i-po];
else //第二种情况,要继续匹配才能得到Extendend[i]的值
{
j=extend[po]+po-i;
if(j<0) j=0; //如果i>Extendend[po]+po则要从头开始匹配
while(i+j<lena&&j<lenb&&a[j+i]==b[j]) j++; extend[i]=j;
po=i; //更新po的位置
}
}
}
int main()
{
int T,cas=0;
scanf("%d",&T);
while(T--)
{
scanf("%s",b);
m=strlen(b);
for(int i=0;i<m;i++) a[i]=a[i+m]=b[i];
n=m+m;
GetNxt(b,m,nxt);
ExKMP(a,n,b,m);
int les=0,equ=0,mre=0;
int k=init(b,m,nt);
for(int i=0;i<k;i++)
{
if(extend[i]==m) {equ++;continue;}
char s=a[i+extend[i]];
char t=b[extend[i]];
if(s<t) les++;
else mre++;
}
printf("Case %d: %d %d %d\n",++cas,les,equ,mre);
}
return 0;
}
963

被折叠的 条评论
为什么被折叠?



