题目链接:https://vjudge.net/problem/HDU-3336
Count the string
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 11760 Accepted Submission(s): 5479
s: "abab"
The prefixes are: "a", "ab", "aba", "abab"
For each prefix, we can count the times it matches in s. So we can see that prefix "a" matches twice, "ab" matches twice too, "aba" matches once, and "abab" matches once. Now you are asked to calculate the sum of the match times for all the prefixes. For "abab", it is 2 + 2 + 1 + 1 = 6.
The answer may be very large, so output the answer mod 10007.
For each case, the first line is an integer n (1 <= n <= 200000), which is the length of string s. A line follows giving the string s. The characters in the strings are all lower-case letters.
题解:
求每个前缀在字符串中出现(可重叠)的次数之和。next数组的应用。
1.求出该字符串的next数组。
2.枚举每个位置,然后用next数组对其进行回退,且每回退一次,都有一个前缀出现了一次(next数组的特性)。
代码如下:
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <cstdlib> 5 #include <string> 6 #include <vector> 7 #include <map> 8 #include <set> 9 #include <queue> 10 #include <sstream> 11 #include <algorithm> 12 using namespace std; 13 typedef long long LL; 14 const double eps = 1e-6; 15 const int INF = 2e9; 16 const LL LNF = 9e18; 17 const int MOD = 1e9+7; 18 const int MAXN = 2e5+10; 19 20 char x[MAXN], y[MAXN]; 21 int Next[MAXN]; 22 23 void get_next(char x[], int m) 24 { 25 int i, j; 26 j = Next[0] = -1; 27 i = 0; 28 while(i<m) 29 { 30 while(j!=-1 && x[i]!=x[j]) j = Next[j]; 31 Next[++i] = ++j; 32 } 33 } 34 35 int main() 36 { 37 int T, n; 38 scanf("%d", &T); 39 while(T--) 40 { 41 scanf("%d%s", &n, x); 42 get_next(x, n); 43 44 int sum = 0; 45 for(int i = 1; i<=n; i++) 46 for(int j = i; j>=1; j = Next[j]) 47 sum = (sum+1)%10007; 48 49 printf("%d\n", sum); 50 } 51 }
51Nod 1277 字符串中的最大值
题目链接:http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1277
输入字符串S, (1 <= L <= 100000, L为字符串的长度),S中的所有字符均为小写英文字母。
输出所有前缀中字符长度与出现次数的乘积的最大值。
abababa
10
题解:
与上一题类似,只不过此题用next数组回退的方法会超时,因为假如字符串是“aaaaaaaaaa”,那么next每回退一次只会退一个,所以枚举每个位置然后next数组回退的方法,时间复杂度最坏可达O(n^2),所以容易超。故采用递推的方法,时间复杂度为O(n),详情请看代码。
代码如下:
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <cstdlib> 5 #include <string> 6 #include <vector> 7 #include <map> 8 #include <set> 9 #include <queue> 10 #include <sstream> 11 #include <algorithm> 12 using namespace std; 13 typedef long long LL; 14 const double eps = 1e-6; 15 const int INF = 2e9; 16 const LL LNF = 9e18; 17 const int MOD = 1e9+7; 18 const int MAXN = 2e5+10; 19 20 char x[MAXN]; 21 int Next[MAXN]; 22 23 void get_next(char x[], int m) 24 { 25 int i, j; 26 j = Next[0] = -1; 27 i = 0; 28 while(i<m) 29 { 30 while(j!=-1 && x[i]!=x[j]) j = Next[j]; 31 Next[++i] = ++j; 32 } 33 } 34 35 int sum[MAXN]; 36 int main() 37 { 38 while(scanf("%s", x)!=EOF) 39 { 40 int n = strlen(x); 41 get_next(x, n); 42 43 memset(sum, 0, sizeof(sum)); 44 for(int i = n; i>=1; i--) 45 { 46 sum[i]++; 47 sum[Next[i]] += sum[i]; 48 } 49 LL ans = 0; 50 for(int i = 1; i<=n; i++) 51 ans = max(ans, 1LL*i*sum[i]); 52 printf("%lld\n", ans); 53 } 54 }