HDU-4821-String(哈希)
题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=4821
题意:
- 输入一个 m m 和
- 再输入一个字符串 s s
问:在 串有多少个子串满足以下条件
(1)该子串的长度为 m∗l m ∗ l
(2)该子串被分为连续的 m m 块长度为 的子串,且每块子串两两互不相同
数据范围:
字符串长度字符串长度字符串长度字符串长度<1e5 字 符 串 长 度 字 符 串 长 度 字 符 串 长 度 字 符 串 长 度 < 1 e 5
字符串长度字符串长度字符串长度1<m∗l<字符串长度 字 符 串 长 度 字 符 串 长 度 字 符 串 长 度 1 < m ∗ l < 字 符 串 长 度
输入:
3 3
abcabcbcaabc
输出:
2
解题思路:
哈希 + 尺取法
哈希: 正常哈希
哈希时相当于把每一个字符看做在一个base进制下的每一位,当然base要大于每一个位的值;
比如:有一个字符串 str = "34534";
为了简单起见,我们就当每一位的值就等于本身显示的整数值,比如"3"等于str[i] = 3;
并且把base定位10,也就是十进制;
那么has公式为:
has[i] = has[i - 1] * base + str[i];
所以:
has[1] = 3;
has[2] = 34;
has[3] = 345;
has[4] = 3453;
has[5] = 34534;
那如果我们求区间[4,5]的哈希值应该怎么求呢?;
我们可以:
has[5] - has[3] * 10的(区间长度)次幂 = 34534 - 345 * 10的2次幂 = 34;
所以要求区间[l,r]的哈希值公式为:
has[r] - has[l - 1] * (base ^ 区间长度);
尺取: 外层循环 i i 从 枚举到 l l ,表示以 位置为开始每距离 l l 分一块,然后尺取,看是否有满足条件的.尺取时,一旦有连续的块个数大于 时,说明满足条件,答案加一.
AC代码:
/********************************************
*Author* : dwh
*Created Time* : 2018年07月10日 星期二 10时08分43秒
*********************************************/
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <stack>
using namespace std;
typedef pair<int, int> PII;
typedef long long LL;
typedef unsigned long long ULL;
#define lookln(x) cout << #x << "=" << x << endl
const int INF_INT=0x3f3f3f3f;
const long long INF_LL=0x7fffffff;
const int maxn = 1e5 + 5;
const ULL base = 277;
LL a[maxn];
ULL has[maxn + 5];
ULL b[maxn + 5];
char str[maxn];
int m,l;
map<ULL,int> vis;
int main(){
b[0] = 1;
for(int i = 1;i < maxn;i++) b[i] = b[i - 1] * base;
while(~scanf("%d %d",&m,&l)){
scanf("%s",str + 1);
int len = strlen(str + 1);
for(int i = 1;i <= len;i++) has[i] = has[i - 1] * base + str[i];
int ans = 0;
for(int i = 1;i <= l;i++){
int cnt = 0;
vis.clear();
for(int j = i;j + l - 1 <= len;j += l){
a[++cnt] = has[j + l - 1] - has[j - 1] * b[l];
}
/*for(int j = 1;j <= cnt;j++){
printf("%llu ",a[j]);
}
printf("\n");
*/
bool flag = 0;
int r = 0;
while(r < cnt && !flag){
r++;
vis[a[r]]++;
if(vis[a[r]] > 1) flag = 1;
}
if(flag == 1){
vis[a[r]]--;
flag = 0;
r--;
}
if(r >= m) ans++;
for(int j = 2;j <= cnt;j++){
vis[a[j - 1]]--;
while(r < cnt && !flag){
r++;
vis[a[r]]++;
if(vis[a[r]] > 1) flag = 1;
}
if(flag == 1){
vis[a[r]]--;
flag = 0;
r--;
}
int all = r - j + 1;
if(all >= m) ans++;
}
}
printf("%d\n",ans);
}
return 0;
}