Revolving Digits HDU - 4333(扩展kmp)

本文介绍了一种利用KMP算法解决循环节问题的方法,通过将原始数字串复制并拼接,形成AA串,然后利用KMP算法找到与AA串的最长公共前缀,从而判断在数字串循环移动过程中,哪些数比原数大、小或相等。文章详细解释了KMP算法的实现,并提供了一个完整的C++代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意

给你一个数字A每次将A的末尾的数往最前面调,多次操作之后回来原来的数问你在这一个过程中比原来的A大,小,等的数各有多少

思路

将A复制一遍插入A的末尾变成AA,只需要在AA中遍历A长度的字符串对于每次的i我们判断i+len(A)长度的数字与A做比较就可以,朴素的比较是n2,我们只需要求出AA与AA的扩展kmp,extend就是与AA的最长公共前缀,如果extend[i]>=len(A)说明两者相等,如果小于只需要比较AA[i+extend[i]]与AA[extend[i]]的大小就可以。

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
#define MAXNUM 1000050
char s1[2*MAXNUM], s2[2*MAXNUM];//s2为待匹配字符串
int s1size, s2size;
//num[i]保存s2长度为i的前缀子串在s1中出现的数目,非必需
int num[2*MAXNUM];
//extend为s1串中对应位置开始匹配的最大前缀长度
//snext为s2串中[i,len]与s2的最长公共前缀长度
int snext[2*MAXNUM],extend[2*MAXNUM];
void getextnext() 
{
	int i, length = s2size;
	snext[0] = length;
	for (i = 0; i<length - 1 && s2[i] == s2[i + 1]; i++);
	snext[1] = i;
	int a = 1;
	for (int k = 2; k < length; k++) 
	{
		int p = a + snext[a] - 1, L = snext[k - a];
		if ((k - 1) + L >= p) 
		{
			int j = (p - k + 1)>0 ? (p - k + 1) : 0;
			while (k + j<length && s2[k + j] == s2[j]) j++;// 枚举(p+1,length) 与(p-k+1,length) 区间比较 
			snext[k] = j, a = k;
		}
		else snext[k] = L;
	}
}

void kmpextend() 
{
	s1size = strlen(s1), s2size = strlen(s2);
	getextnext();
	int a = 0;
	int MinLen = s1size>s2size ? s2size : s1size;
	while (a<MinLen && s1[a] == s2[a]) a++;
	extend[0] = a, a = 0;
	for (int i = 1; i < s1size; i++) 
	{
		int p = a + extend[a] - 1, L = snext[i - a];
		if ((i - 1) + L >= p) 
		{
			int j = (p -i + 1)>0 ? (p - i + 1) : 0;
			while (i + j<s1size && j<s2size && s1[i + j] == s2[j]) 
				j++;
			extend[i] = j;
			a = i;
		}
		else extend[i] = L;
	}
    //计算Num,可去
	for (int i = 0; i < s1size; i++)
		num[extend[i]]++;
	for (int i = s2size - 1; i >= 1; i--)
		num[i] += num[i + 1];
}

int XHJ(char str[])
{
	// 已经求出next数组
	int kk;  // kk保存最短循环节
	int len=strlen(str);
	for(int i=1; i<=len; ++i)
	{
    	if(i+snext[i]>=len){
        	kk = len%i ? len : i;
        	break;
    	}
	}
	return kk;
}

char str3[2*MAXNUM];

int main()
{
	int T;cin>>T;
	int kace=1;
	while(T--)
	{
		scanf("%s",str3);
		int len3=strlen(str3);
		for(int i=0;i<len3;i++) s1[i]=str3[i];
		for(int i=0;i<len3;i++) s1[len3+i]=str3[i];
		int len1=2*len3;
		s1[len1]='\0';
		for(int i=0;i<len1;i++) s2[i]=s1[i];
		s2[len1]='\0';
		kmpextend();
		int a=0,b=0,c=0;
		int kk=XHJ(s1);
		for(int i=0;i<kk;i++)
		{
			if(extend[i]>=kk) b++;
			else
			{
				if(s1[i+extend[i]]>s1[extend[i]]) c++;
				else if(s1[i+extend[i]]<s1[extend[i]]) a++;
			}
		}
		printf("Case %d: %d %d %d\n",kace++,a,b,c);
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值