HDU - 4821 String(hash+尺取)

Given a string S and two integers L and M, we consider a substring of S as “recoverable” if and only if
(i) It is of length M*L;
(ii) It can be constructed by concatenating M “diversified” substrings of S, where each of these substrings has length L; two strings are considered as “diversified” if they don’t have the same character for every position.

Two substrings of S are considered as “different” if they are cut from different part of S. For example, string “aa” has 3 different substrings “aa”, “a” and “a”.

Your task is to calculate the number of different “recoverable” substrings of S.
Input
The input contains multiple test cases, proceeding to the End of File.

The first line of each test case has two space-separated integers M and L.

The second ine of each test case has a string S, which consists of only lowercase letters.

The length of S is not larger than 10^5, and 1 ≤ M * L ≤ the length of S.
Output
For each test case, output the answer in a single line.
Sample Input
3 3
abcabcbcaabc
Sample Output
2
题意: 题意就比较难理解,通俗来说就是给出m,l,和一段字符串,所求为该字符串中某种子串的个数;
某种子串为:

  1. 长度为m*l;
  2. 由m个长为l的子子串构成并且每个子子串互不相同。

**思路:**思路的话,刚开始考虑的为一个字符一个字符的移动,而这种思路当l为1并且字符串长度很长的时候最坏复杂度为O(n^2)肯定不行;
这里就要用一种巧妙的移动方式,尺取。
之前做过一个尺取字符串的题,和这个处理方式很像,这里是博客: https://blog.csdn.net/nb_weige/article/details/101940000
尺取方法在注释
代码:

//#include
#include
#include
#include
#include
using namespace std;
const int maxn=1e5+7;
#define ll long long
#define ull unsigned long long
ull base[maxn];
ull Hash[maxn];
char s[maxn];
void gethash(char *p)
{
     
	Hash[0]=0;
	ll len=strlen(p);
	for(int i=1;i<=len;i++)
	{
     
		Hash[i]=Hash[i-1]*131+p[i];
	}
}
ull fun(int l,int r)
{
     
	return Hash[r]-Hash[l-1]*base[r-l+1];
}
map<ull,int>mp;
int main()
{
     
	base[0]=1;
	for(int i=1;i<=maxn;i++)
		base[i]=base[i-1]*131;
	ll l,m;
	while(~scanf("%lld%lld",&m,&l))
	{
     
		ll num=0;
		scanf("%s",s+1);
		ll len=strlen(s+1);
		Hash[0]=0;
		for(int i=1;i<=len;i++)
			Hash[i]=Hash[i-1]*131+s[i];
		for(int i=1;i<=l&&i+m*l-1<=len;i++)     //只需要遍历到 l ,因为后面的尺取已经遍历过
		{
     
			mp.clear();
			for(int j=i;j<i+l*m;j+=l)
				mp[fun(j,j+l-1)]++;
			if(mp.size()==m)                    //找到第一个满足条件的,然后尺取
				num++;
			for(int j=i+m*l;j+l-1<=len;j+=l)
			{
     
				mp[fun(j,j+l-1)]++;                     //尺取: 向后延伸,加上后面的长 l 的串
				mp[fun(j-m*l,j-m*l+l-1)]--;             //尺取:向前看,去掉前面的一个长 l 的串
				if(mp[fun(j-m*l,j-m*l+l-1)]==0)
					mp.erase(fun(j-m*l,j-m*l+l-1));
				if(mp.size()==m)
					num++;
			}
		}
		printf("%lld\n",num);
	}
	return 0;
}

你可能感兴趣的:(尺取,字符串,HDU-4821,String)