题意:
给定一个字符串,然后将字符串最后的字母不断移动到字符串的首字母位置,需要求出在这个移动的过程中,生成的字符串比原串字典序大、相等、小的所有字符串个数。移动过程中,相同的字符串不重复计算。
思路:
由于是比较字典序,因此需要进行前缀比较。自然地想到要使用扩展kmp知识,我们这里先大概回忆一下扩展kmp知识。
next [ i ] : 模板串从 i 开始截取的子串与原串的最大前缀匹配长度
ex [ i ] : 匹配串从 i 开始截取的子串与模板串的最大前缀匹配长度
可以用线性时间求出这两个数组。
我们再来看这个题,就变成了一个水题。就是将这个串*2拼在一起,然后对于这个新串求一遍next数组即可。现在问题在于如何处理 “相同的字符串不重复计算” 这个问题,很自然地可以想到循环节。
令 cnt = 串S循环节在串S中出现了几次。
则对于这个串可能出现的所有串中,每个串出现的重复次数为cnt。因此最后的答案需要除以cnt。
扫描二维码关注公众号,回复:
4129447 查看本文章
总结:
扩展KMP就是用来计算前缀匹配长度的,应用于各类前缀匹配问题。
代码:
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#define rep(i,a,b) for(int i = a; i <= b; i++)
using namespace std;
typedef long long ll;
const int INF = 2147483640;
const double eps = 1e-8;
const int N = 3*100000+100; //字符串长度最大值
char a[N];
int nxt[N]; //ex数组即为extend数组
//预处理计算next数组
int get(char* tp)
{
int nxtt[N];
char s[N];
memset(nxtt,0,sizeof nxtt);
int l = strlen(tp);
for(int i = l+1; i >= 1; i--) s[i] = tp[i-1];
int j = 0;
for(int i = 2;i <= l;i ++)
{
while(j && s[j+1] != s[i]) j = nxtt[j];
if(s[j+1] == s[i]) j ++;
nxtt[i] = j;
}
return l-nxtt[l];
}
void GETNEXT(char *str)
{
int i=0,j,po,len=strlen(str);
nxt[0]=len;//初始化next[0]
while(str[i]==str[i+1]&&i+1<len)//计算next[1]
i++;
nxt[1]=i;
po=1;//初始化po的位置
for(i=2;i<len;i++)
{
if(nxt[i-po]+i<nxt[po]+po)//第一种情况,可以直接得到next[i]的值
nxt[i]=nxt[i-po];
else//第二种情况,要继续匹配才能得到next[i]的值
{
j=nxt[po]+po-i;
if(j<0)j=0;//如果i>po+next[po],则要从头开始匹配
while(i+j<len&&str[j]==str[j+i])//计算next[i]
j++;
nxt[i]=j;
po=i;//更新po的位置
}
}
}
int main(int argc, char const *argv[])
{
int T;
scanf("%d",&T);
rep(kk,1,T)
{
scanf("%s",a);
int len = strlen(a);
int tp = get(a);
int ans = len/tp;
// if(len % tp == 0) ans = len/tp;
rep(i,len,2*len-1)
a[i] = a[i-len];
a[2*len] = '\0';
GETNEXT(a);
int c1 = 0,c2 = 0,c3 = 0;
rep(i,0,len-1)
{
if(a[i] == '0'){
c1++;
continue;
}
int tp = nxt[i];
if(tp >= len) c2++;
else{
if(a[tp]-'0' < a[i+tp]-'0') c3++;
else c1++;
}
}
printf("Case %d: ",kk);
printf("%d %d %d\n",c1/ans,c2/ans,c3/ans);
}
return 0;
}
/*
字符串从0开始编号
next[i]: 模板串从i开始截取的子串与原串的最大前缀匹配长度
ex[i]: 匹配串从i开始截取的子串与模板串的最大前缀匹配长度
*/