hdu 4821 字符串hash + map判重

来源:互联网 发布:上海师范大学网络课程 编辑:程序博客网 时间:2024/06/06 09:54


String

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 3458    Accepted Submission(s): 1050


Problem Description
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 3abcabcbcaabc
 

Sample Output
2
 

Source
2013 Asia Regional Changchun

题意:给定一个字符串,把它分为多个长度为M*L的子串,对于每个子串要求再把它分为M个长度为L的小子串,对于这M个长度为L的小子串要求任意两个都不相同。问能够分成多少个这样的长度为M*L的子串。

思路:

第一次写的时候,用hash暴力从每个开始点判断一个长度为M*L的子串是否符合条件。但是超时了,时间复杂度为O(n^2)。

后来看了别人的思路,发现可以将总的字符串分为L个序列,对于每个序列,如果已经判断了一个,那么只要将判断过的子串去掉开头的L个字符,然后在子串的后面再加上L个字符,就是一个新的子串,这样实际上对于每个序列里,长度为L的字符串都只计算了一遍。对于暴力的话,要计算多次,浪费了时间。(可能有点绕)

代码:

#include<algorithm>#include<iostream>#include<cstdio>#include<string.h>#include<cmath>#include<map>using namespace std;typedef unsigned long long ull;const int N = 1e5+5;const int X = 163;ull hhash[N], p[N];char str[N];int m, l;map<ull, int> Map;void inIt() {p[0] = 1;for(int i = 1; i <= N- 5; i++)p[i] = p[i-1] * X;}ull get(int l, int r, ull g[]) {return g[r] - g[l-1]*p[r-l+1];}int main() {int length, flag;int number;ull sum, temp;int count, i ,j;inIt();while(scanf("%d%d", &m, &l) != EOF) {scanf("%s", str);length = strlen(str);number = 0;hhash[0] = 0;for(i = 1; i <= length; i++)hhash[i] = hhash[i-1]*X + (str[i-1]- 'a');for(i = 1; i <= l && i <= length - m*l; i++) { //既然分为了L个序列,为什么还要加上 Map.clear();//i<=length-m*l这个条件呢?原因很简单,有可能不够L个序列 for(j = i; j < i+m*l-1; j+=l) {//假设有L=3个序列 ,m=4,字符串长度为12, temp = get(j, j+l-1, hhash);//其实只有该字符串本身可能满足条件,但是这里却要 Map[temp]++;//循环3次,多了,所以要加上上面的条件。 } if(Map.size() == m)number++;int head;for(; j+l-1 <= length; j+= l) {head = j - m*l;  //去掉开头的L个字符。 temp = get(head, head+l-1, hhash);Map[temp]--;if(Map[temp] == 0) {Map.erase(temp);}temp = get(j, j+l-1, hhash);//加上后面的L个字符。 Map[temp]++;if(Map.size() == m)number++;}}printf("%d\n", number);}}


原创粉丝点击