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<bits/stdc++.h>
#include<stdio.h>
#include<algorithm>
#include<map>
#include<string.h>
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;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章