2 abcabcabca 4 abcabcabcabc 3
0 55
/************************************************************************/
附上该题对应的中文题
有一个 10≤长度≤1,000,000 的字符串,仅由小写字母构成。求有多少个子串,包含有至少k(1≤k≤26)个不同的字母?
输入包含多组数据. 第一行有一个整数T(1≤T≤10), 表示测试数据的组数. 对于每组数据: 第一行输入字符串S。 第二行输入一个整数k。
对于每组数据,输出符合要求的子串的个数。
2 abcabcabca 4 abcabcabcabc 3
0 55
出题人的解题思路:
有一个明显的性质:如果子串(i,j)包含了至少k个不同的字符,那么子串(i,k),(j<k<length)也包含了至少k个不同字符。
因此对于每一个左边界,只要找到最小的满足条件的右边界,就能在O(1)时间内统计完所有以这个左边界开始的符合条件的子串。
寻找这个右边界,是经典的追赶法(尺取法,双指针法)问题。维护两个指针(数组下标),轮流更新左右边界,同时累加答案即可。复杂度 O(length(S))。
/*Sherlock and Watson and Adler*/
#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<queue>
#include<stack>
#include<math.h>
#include<vector>
#include<map>
#include<deque>
#include<set>
#include<cmath>
#include<complex>
#include<string>
#include<algorithm>
#include<iostream>
#define exp 1e-10
#define ll long long
using namespace std;
const int N = 1000005;
const int M = 10000;
const int inf = 100000000;
const int mod = 2009;
char s[N];
int v[30];
int main()
{
int t,m,k,p,i,j,len,c;
__int64 sum;
int n;
scanf("%d",&t);
while(t--)
{
memset(v,0,sizeof(v));
scanf("%s%d",s,&n);
len=strlen(s);
sum=c=p=0;
for(i=0;i<len;i++)
{
if(!v[s[i]-'a'])
c++;
v[s[i]-'a']++;
if(c >= n)
{
sum=sum+len-i ;
for(j=p;j<len;++j)
{
v[s[j]-'a']--;
if(!v[s[j]-'a'])
c--;
if(c<n)
break;
sum=sum+len-i ;
}
p=j+1;
}
}
printf("%I64d\n",sum);
}
return 0;
}
菜鸟成长记