世界真的很大
对于把这道题当做扩展kmp的入门题的人们,有几句话我是一定要说的。
这道题不用求extand!
这道题不用求extand!
这道题不用求extand!
看了网上的几篇博客之后一直是晕的,不知怎么的他们的exkmp函数和我理解的kmp都不一样,以至于我都开始怀疑自己的理解了。。。
自己静下心来,好好分析了一遍题目后才发现,被坑了。其实根本没有求exkmp的步骤,仅对于这道题而言,求出nxt数组就足够了,看完题后且听我娓娓道来:
description:
因为原题是ENGLISH就不复制了,大概意思是,多组数据,每组数据给你一个数字,(<10^1000000,很显然只能用字符串了),每次把数字最高位的数挪到最低位,问在整个过程中,出现的数比原数大的有几个,等于的有几个,小于的有几个
input
第一行一个整数T
之后T行,每行一个整数n
output:
T行
每行输出 Case i: a b c 表示数据组数,大于的个数,小于的个数,大于的个数。
分析题意,想要比较交换后数字和原数的大小,因为数字是从最高位比较起,找到第一个不同的数比较大小关系,换句话说就是找到最长公共前缀,再比较后一位的大小。
大概意思明白了,现在来处理把首位数挪到末尾的问题,其实可以转化成把两个相同的字符串拼到一起,再任意长度为原长的区间里,其实都可以看做把原数的前面一截粘到后面。
那就是说求出拼成的字符串的每一个后缀和原字符串的最长公共前缀,想到扩展kmp是对的,但是如果去写扩展kmp就亏大了
新拼成的字符串和原串几乎是一样的,换句话说就是求新串自己的每一个后缀和自己本身不超过长度一半的最长公共前缀。扩展kmp里不是有一个求next数组的过程吗?用那个就行了。网上好多人把求nxt直接打成exkmp,让我想了好久。。。
这个求nxt数组的函数是可以当做模板来用的,
完整代码:
#include<stdio.h>
#include<cstring>
#include<algorithm>
using namespace std;
char t[400010];
int nxt[500010],exd[500010];
int m,tt,ans,bns,cns,t0t;
void calnxt(char *T)// nxt[i]: 以第i位置开始的子串 与 T的公共前缀
{
int i,length = strlen(T);
nxt[0] = length;
for(i = 0;i<length-1 && T[i]==T[i+1]; i++);
nxt[1] = i;
int a = 1;
for(int k = 2; k < length; k++)
{
int p = a+nxt[a]-1, L = nxt[k-a];
if( (k-1)+L >= p )
{
int j = (p-k+1)>0? (p-k+1) : 0;
while(k+j<length && T[k+j]==T[j]) j++;// 枚举(p+1,length) 与(p-k+1,length) 区间比较
nxt[k] = j, a = k;
}
else nxt[k] = L;
}
}
int main()
{
scanf("%d",&t0t);
for(int e=1;e<=t0t;e++)
{
memset(nxt,0,sizeof(nxt));
memset(exd,0,sizeof(exd));
ans=bns=cns=0;
scanf("%s",t);
m=strlen(t);
for(int i=0;i<m;i++) t[i+m]=t[i];
t[m+m]='#';
calnxt(t);
for(int i=0;i<m;i++)
{
if(nxt[i]>=m) ans++;
else if(t[nxt[i]]>t[i+nxt[i]]) bns++;
else if(t[nxt[i]]<t[i+nxt[i]]) cns++;
}
printf("Case %d: %d %d %d\n",e,bns/ans,1,cns/ans);
}
}
嗯,就是这样。。