对于一个字符串 S,我们定义 S 的分值 f(S) 为 S 中出现的不同的字符个数。例如 f("aba")=2,f("abc")=3, f("aaa")=1。
现在给定一个字符串 S[0…n−1](长度为 n),请你计算对于所有 S 的非空子串 Si…j,f(S[i…j])的和是多少。
输入格式输入一行包含一个由小写字母组成的字符串 S。
输出格式输出一个整数表示答案。
样例输入ababc样例输出
28样例说明
子串 f值 a 1 ab 2 aba 2 abab 2 ababc 3 b 1 ba 2 bab 2 babc 3 a 1 ab 2 abc 3 b 1 bc 2 c 1评测用例规模与约定
对于 20% 的评测用例,1≤n≤10;
对于 40% 的评测用例,1≤n≤100;
对于 50% 的评测用例,1≤n≤1000;
对于 60% 的评测用例,1≤n≤10000;
对于所有评测用例,1≤n≤100000。
解题方法 1.暴力枚举法——时间复杂度(O3)- 遍历所有子串找出每个子串里面不同字符的个数将所有子串不同字符的个数相加
这里用集合的方法找出每个字符串不同字符的个数,引入set库,申请一个集合,将字符串中的每个字符添加进去,最后返回集合的大小。
此方法时间复杂度很高,因此会超时。可拿40%的分数。
代码如下:
// // Created by Charon on 2022/1/23. // #include "cstring" #include "iostream" #include "set" using namespace std; #define MAX 100000 //暴力枚举法 超时 int sum(int min, int max, char s[]) { set2.乘法原理——时间复杂度(O)st; //申请一个集合st。 for (int i = min; i <= max; ++i) { st.insert(s[i]); //在集合中插入元素 } return st.size(); } int main() { char s[MAX]; cin >> s; int n = 0; for (int i = 0; i < strlen(s); ++i) { for (int j = i; j < strlen(s); ++j) { n += sum(i, j, s); } } cout << n << endl; return 0; }
- 每个字母只有在第一次出现时才有贡献度,因此可以统计每个字母在第一次出现的情况下,能被多少子串所包含;用 last[s[i]] 记录字母 s[i] 上一次出现的位置;那么往左最多能延伸到 last[s[i]] + 1,其到第 i 个字母一共有 i - last[s[i]] 个字母;
同理往右最多能延伸到 n,其到第 i 个字母一共有 n - i 个字母;二者相乘,就是该字母被不同子串所包含的总次数;
代码如下:
// // Created by Charon on 2022/1/23. // #include "cstring" #include "iostream" #include "set" using namespace std; #define MAX 100000 //乘法原理 int main() { int last[256]; memset(last, -1, sizeof(last)); string s; cin >> s; int n = s.size(); long long counts = 0; for (int i = 0; i < n; ++i) { counts += (long long) (i - last[s[i]]) * (n - i); last[s[i]] = i; } cout << counts << endl; return 0; }
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)