Problem Description:
You are given a string S consisting of lowercase letters, and your task is counting the number of substring that the number of each lowercase letter in the substring is no more than K.
Input:
In the first line there is an integer T , indicates the number of test cases.
For each case, the first line contains a string which only consist of lowercase letters. The second line contains an integer K.
[Technical Specification]
1<=T<= 100
1 <= the length of S <= 100000
1 <= K <= 100000
Output:
For each case, output a line contains the answer.
Sample Input:
3
abc
1
abcabc
1
abcabc
2
Sample Output:
6
15
21
题意:给出一个字符串s和一个整数k,问有多少s的子串满足条件,条件是子串中每个字母出现的个数不能大于k(只有小写字母)。
这里用到一个公式,一个长度为n的字符串的连续子串的个数为1+2+3+……+n。证明:举例如abcabc:我们可以分别得到该字符串长度为1,2,3,4,5,6的子串:
1:a b c a b c;
2:ab bc ca ab bc;
3:abc bca cab abc;
4:abca bcab cabc;
5:abcab bcabc;
6:abcabc;
不难发现连续子串的个数和是1+2+3+……+n。
#include<stdio.h> #include<string.h> #include<queue> #include<math.h> #include<stdlib.h> #include<algorithm> using namespace std; const int N=1e5+10; const int INF=0x3f3f3f3f; const int MOD=1e9+7; typedef long long LL; char s[N]; int vis[N]; ///标记序号为i的字母出现的次数 int main () { int T, k, i, j; LL ans; ///这里必须用LL,不然会wa scanf("%d", &T); while (T--) { scanf("%s %d", s, &k); j = 0; ///当发现一个字母出现的次数>k时,j的值会改变 ans = 0; memset(vis, 0, sizeof(vis)); for (i = 0; s[i] != ‘\0‘; i++) { vis[s[i]-‘a‘]++; if (vis[s[i]-‘a‘] > k) { while (s[j] != s[i]) ///举例:bcaacb(k==1),当我们找到第二个a时发现a出现的次数大于k了,首先之前的bca已经算出了子串个数1+2+3,所以现在的bc的次数需要减1,j需要移到前一个a { vis[s[j]-‘a‘]--; j++; } vis[s[i]-‘a‘]--; ///还是上面的例子由于现在a的次数大于k,那么我们也需要将a的次数减1,j指向现在的a(当然举例:abcabc也是成功的,只是这时没有经过while循环那一步) j++; } ans += i-j+1; ///这里计算子串的个数,至于为什么只加上字符的个数,举例:abc:在a的时候我们会加1(可以看做加上了a这个子串),在b的时候加2(本来ab有三个子串:a,b,ab,但是之前加了a,不能重复,所以加2),那么在c的时候同理加3就好了 } printf("%lld\n", ans); } return 0; }