HDU-4333 Revolving Digits(KMP,扩展KMP)

本文介绍了一种通过旋转数字的不同部分来生成新数字,并比较这些数字与原始数字大小的算法。该算法利用KMP算法的next数组和扩展KMP算法的extend数组来高效地找出旋转后数字与原数字的关系,包括小于、等于和大于原数字的数量。

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

Revolving Digits

Problem Description

One day Silence is interested in revolving the digits of a positive integer. In the revolving operation, he can put several last digits to the front of the integer. Of course, he can put all the digits to the front, so he will get the integer itself. For example, he can change 123 into 312, 231 and 123. Now he wanted to know how many different integers he can get that is less than the original integer, how many different integers he can get that is equal to the original integer and how many different integers he can get that is greater than the original integer. We will ensure that the original integer is positive and it has no leading zeros, but if we get an integer with some leading zeros by revolving the digits, we will regard the new integer as it has no leading zeros. For example, if the original integer is 104, we can get 410, 41 and 104.

Input

The first line of the input contains an integer T (1<=T<=50) which means the number of test cases.
For each test cases, there is only one line that is the original integer N. we will ensure that N is an positive integer without leading zeros and N is less than 10^100000.

Output

For each test case, please output a line which is “Case X: L E G”, X means the number of the test case. And L means the number of integers is less than N that we can get by revolving digits. E means the number of integers is equal to N. G means the number of integers is greater than N.

Sample Input

1
341

Sample Output

Case 1: 1 1 1

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=4333

题意:

给你一个T,有T组数据,每组数据给你一个n,问你把n后面的数移到前面形成的数比原来n大还是小还是相等。输出小于n,等于n,大于n的个数。例如:n = 123,首先可以把23移到1前面形成231比原来大,还可以把3移到前面形成312比原来大,还可以不移动还是123和原来相等。(形成的数中不可以存在重复,例如1111,只能产生一个1111).

思路:

KMP中的next[]数组指的是模式串中最长公共前后缀。x[i-next[i]…i-1] = x[0…next[i]-1]。例如123123,next[6] = 3。例如12121212,next[8]=6.
如果不理解KMP算法可以看一下(强烈推荐https://www.bilibili.com/video/av11866460

扩展KMP中的next数组表示模式串中x[i…m-1]与x[0…m-1]最长公共前缀(与KMP中的next数组不是一回事)。
扩展KMP中的extend[]数组表示T与S[i,n-1]的最长公共前缀。举个例子:在这里插入图片描述 主串S,模式串T,extend[0] = 4, extend[1] = 3, extend[2] = 2,extend[3]=1, extend[4] = 0, extend[5] = 2。

说了这么多,跟这个题有毛关系呢。。。
这个题利用的是extend数组,首先把串复制到后面一次,例如123,即123123。这样是为了求每一位的后缀与自身的最长公共前缀。还是举个例子。
例如341,这时让主串为复制后的341341,模式串为341。

在这里插入图片描述
extend[1] 就是让413和341进行匹配,此时extend[1]=0,如果这时不是全匹配,那么就肯定不是相等的,只需判定s[i+extend[i]]与t[extend[i]]谁大谁小即可。
在这里插入图片描述
extend[2] = 0,判断s[2]与t[0]谁大谁小。
在这里插入图片描述
这时extend[3]=3,此时extend[3] = m,那么他是全匹配的,所以是相等的。
就是利用extend数组这样的特性来求出的。不过还要注意有循环节的情况。例如12121212和1111。
12121212
21212121
12121212
21212121

剩下的全部都是重复的了,还需要用KMP中的next数组来求出他的循环节。例如12121212,next[n]=6。如果n%(n-next[n])==0,说明这时候里面是有循环节,是有重复的。重复的次数为n/(n-next[n])次,所以最后个数除以n/(n-next[n])就可以。。。。。

代码:

#include<bits/stdc++.h>
using namespace std;

void kmp_pre(string s, int m, int NEXT[]) 
{
	int i, j;
	j = NEXT[0] = -1;
	i = 0;
	while(i < m) {
		while(-1 != j && s[i] != s[j]) 
			j = NEXT[j];
		NEXT[++i] = ++j;	
	}
}
void pre_EKMP(string t, int m, int Next[])
{
	Next[0] = m;
	int j = 0;
	while( j + 1 < m && t[j] == t[j+1]) j++;
	Next[1] = j;
	int k = 1;
	for(int i = 2; i < m; i++) {
		int p = Next[k] + k-1;
		int L = Next[i-k];
		if(i + L < p+1) Next[i] = L;
		else {
			j = max(0, p-i+1);
			while(i+j<m && t[i+j] == t[j]) j++;
			Next[i] = j;
			k = i;
		}
	}
		
}
void EKMP(string t, int m, string s, int n, int Next[], int extend[])
{

	pre_EKMP(t, m ,Next);
	int j = 0;
	while(j < n && j < m && t[j] == s[j]) j++;
	extend[0] = j;
	int k = 0;
	for(int i = 1; i < n; i++)
	{
		int p = extend[k] + k-1;
		int L = Next[i-k];
		if(i+L < p+1) extend[i] = L;
		else {
			j = max(0, p-i+1);
			while(i+j<n && j < m && s[i+j] == t[j]) j++;
			extend[i] = j;
			k = i;
		}
		
	}
}
int main()
{
	int p;
	cin >> p;
	int Case = 0;
	while(p--) {
		string s, t;
		cin >> t;
		s = t;
		s += t;
		int m = t.length();
		int n = s.length();
		int extend[100005];
		int Next[100005];
		int NEXT[100005];
		memset(extend, 0, sizeof(extend));
		memset(Next, 0, sizeof(Next));
		memset(NEXT, 0, sizeof(NEXT));
		kmp_pre(s, m, NEXT);
		EKMP( t, m,  s, n, Next, extend);
		int L, E, G;
		L= E =G=0;
		for(int i = 0; i < n-m; i++) {
			if(extend[i] == m) {
				E++;
			}
			else {
				if(s[i+extend[i]] > t[extend[i]])
					G++;
				else 
					L++;
			}
		}
		if(!(m%(m-NEXT[m]))) {
			int k = m / (m-NEXT[m]);
			printf("Case %d: %d %d %d\n", ++Case,L/k, E/k, G/k);
		}
		else {
			printf("Case %d: %d %d %d\n", ++Case,L, E, G);
		}
	
	}
	
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值