题目链接
字符串的 引力 定义为:字符串中 不同 字符的数量。
例如,"abbca" 的引力为 3 ,因为其中有 3 个不同字符 'a'、'b' 和 'c' 。
给你一个字符串 s ,返回 其所有子字符串的总引力 。
子字符串 定义为:字符串中的一个连续字符序列。
示例 1:
输入:s = "abbca"
输出:28
解释:"abbca" 的子字符串有:
- 长度为 1 的子字符串:"a"、"b"、"b"、"c"、"a" 的引力分别为 1、1、1、1、1,总和为 5 。
- 长度为 2 的子字符串:"ab"、"bb"、"bc"、"ca" 的引力分别为 2、1、2、2 ,总和为 7 。
- 长度为 3 的子字符串:"abb"、"bbc"、"bca" 的引力分别为 2、2、3 ,总和为 7 。
- 长度为 4 的子字符串:"abbc"、"bbca" 的引力分别为 3、3 ,总和为 6 。
- 长度为 5 的子字符串:"abbca" 的引力为 3 ,总和为 3 。
引力总和为 5 + 7 + 7 + 6 + 3 = 28 。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/total-appeal-of-a-string
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
所有的连续子数组是[i, j] i <= j 的组合,直接枚举所有情况,是O(N^2)的时间复杂度,
可以考虑使用动态规划来分析,假设f(i)是以第i个元素为起始点的所有情况,
f(0) = [0, 0], [0,1], [0,2], [0,3] ……
f(1) = [1,1], [1,2], [1,3] ……
可以看到,f(1)的结果可以从f(0)推导出来,分为a[0] == a[1]和a[0] != a[1]的情况,
按照这个思路,可以这样写
class Solution {
public:
long long appealSum(string s) {
//init, get sum and each pos
long long f = 0;
map<int, vector<int>> pos;
int len = s.size();
for (int i = 0; i < len; i++) {
int k = s[i];
pos[k].push_back(i);
f += pos.size();
}
long long ans = f;
vector<int> dt(len);
vector<long long> fs(len);
dt[0] = 0;
fs[0] = f;
for (int i = 0; i < len-1; i++) {
//f1 = f0 - dt
int c = s[i];
if (s[i] == s[i+1]) {
fs[i+1] = fs[i] - 1;
ans += fs[i+1];
continue;
}
//find the pos of i
vector<int>& arr = pos[c];
int b = 0;
int e = arr.size()-1;
int p = e+1;
while (b <= e) {
int m = (b+e)/2;
if (arr[m] > i) {
e = m-1;
p = m;
} else {
b = m+1;
}
}
int d = len - i;
if (p <= arr.size()-1) {
d = arr[p] - i;
}
fs[i+1] = fs[i] - d;
ans += fs[i+1];
}
return ans;
}
};
对于这个题目,也可以假设f(i)是以a[i]为末尾的情况,这样处理,程序写起来更清楚一些,
上面的题目是求>=1的个数,下面的题目比较相似,是求==1的数量,采用f(i)是以a[i]为末尾的情况来处理,
class Solution {
public:
int uniqueLetterString(string s) {
const int n = s.size();
int tt = 1000000000+7;
//1.
// dp[i]: 以s[i]结尾的子字符串的总引力
// pre_idx[i]: 记录字符 'a'+i 之前出现的位置,如果之前没有出现,就 = -1
long long dp[n];
vector<vector<int>> pre_idx(26, vector<int>(2, -1));
// int pre_idx[26][2] = {-1};//just the first one is -1
// 2. initial
dp[0] = 1;
int c = s[0] - 'A';
pre_idx[c][1] = 0;
// 3.
long long ans = 1;
for (int i = 1; i < n; ++i) {
c = s[i] - 'A';//////// char of pos i
// int len = i - pre_idx[s[i] - 'A'] - 1;
int dt = 0;
//not appeared begfore
if (pre_idx[c][1] == -1) {
dt = i+1;
} else {
dt = (i-pre_idx[c][1])
- (pre_idx[c][1]-pre_idx[c][0]);
// (pre_idx[c][1]-pre_idx[c][0]) //new
// - (i-pre_idx[c][0]);//old
}
dp[i] = dp[i - 1] + dt;
pre_idx[c][0] = pre_idx[c][1];
pre_idx[c][1] = i;
//
if (dp[i] > tt) {
dp[i] = dp[i]%tt;
}
ans += dp[i];
}
// 4.
return ans%tt;
}
};
另外子数组的题目,需要考虑使用单调栈找左右区间,
该博客探讨了如何计算字符串所有子字符串的引力,即不同字符的数量之和。通过动态规划的方法,实现了从已知子串引力推导出后续子串引力的过程,有效地解决了O(N^2)的时间复杂度问题。示例中详细解释了算法实现,并提供了两个不同思路的代码示例,一个是求引力总和,另一个是寻找特定数量相同字符的子串。此外,还提到了处理子数组问题时可以考虑使用单调栈策略。
285

被折叠的 条评论
为什么被折叠?



