LeetCode/环绕字符串中的唯一子字符串
把字符串 s 看作是 “abcdefghijklmnopqrstuvwxyz” 的无限环绕字符串
现在给定另一个字符串 p 返回 s 中 唯一 的 p 的 非空子串 的数量
看似是两个个字符串进行匹配的题目,实际上还是评估一个串,因为无限环绕串满足特定规律
所以只需提取出字符串p中符合规律的子串即可,无限环绕串满足的是递增的规律
所以我们考虑用动态规划,从前往后,对匹配串进行评估,计算其满足递增的子串数量
假设设dp[i]为前i位字符的递增子串数量,无法得到一个转移方程,因为这时候的dp[i]只告诉了数量,
而丢失了我们对于继续评估后面串的关键信息,也就是失去了无后效性
这里我们dp[i]设为以i结尾的递增字符串的长度,这样我们就能很好的继续评估后面的部分
只需比较dp[i+1]与dp[i]是否是递增关系,如果是,那么会有dp[i+1]=dp[i]+1,否则dp[i+1]=1
我们得到了以所有位置结尾的递增字符串长度,可以根据这些长度来计算递增子串数量
考虑到后面会有重复的子串,而以同一种字符结尾的只需计算一次,所以要用哈希表进行记录
因为字符只有26个且存在先后顺序,我们直接用数组代替哈希表,记录对应字符结尾的最大长度
最后再对所有字符求和即可,算法中用滚动变量k代替了dp数组
class Solution {
public:
int findSubstringInWraproundString(string p) {
//对p串进行评估
//这里的map_不是动态规划表,而是索引表
vector<int> map_(26);
int k = 0;//k为以该字母结尾的递增子串长度
for (int i = 0; i < p.length(); ++i) {//遍历整个长度
if (i && (p[i] - p[i - 1] + 26) % 26 == 1) // 字符之差为 1 或 -25
++k;//如果是递增子串加一,这里只用维护一个变量即可,不用使用数组
else k = 1;//否则置1
map_[p[i] - 'a'] = max(map_[p[i] - 'a'], k);//对该字母最大长度进行更新,避免重复
}
return accumulate(map_.begin(), map_.end(), 0);
}
};