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; }菜鸟成长记