LeetCode 6036. 构造字符串的总得分和
文章目录
- 一、题目
- 1、题目描述
- 2、基础框架
- 3、原题链接
- 二、解题报告
- 1、思路分析
- 2、时间复杂度
- 3、代码详解
- 三、本题小知识
- 四、加群须知
一、题目
1、题目描述
你需要从空字符串开始 构造 一个长度为 nnn 的字符串 sss ,构造的过程为每次给当前字符串 前面 添加 一个 字符。构造过程中得到的所有字符串编号为 111 到 nnn ,其中长度为 iii 的字符串编号为 sisisi 。比方说,
s = "abaca" ,s1 == "a" ,s2 == "ca" ,s3 == "aca"
依次类推。sisisi 的 得分 为 sisisi 和 snsnsn 的 最长公共前缀 的长度(注意s == sn
)。给你最终的字符串 sss ,请你返回每一个 sisisi 的 得分之和 。
样例输入:s = "babab"
样例输出:9
2、基础框架
- C语言 给出的基础框架代码如下:
long long sumScores(char * s){}
3、原题链接
LeetCode 6036. 构造字符串的总得分和
二、解题报告
1、思路分析
(1)(1)(1) 要求 s[i]s[i]s[i] 和 s[n]s[n]s[n] 的最长公共前缀,假设原字符串为 SSS,对于 s[i]s[i]s[i] 表示的是:s[i]=S[n−i+1:n]s[i] = S[n-i+1: n]s[i]=S[n−i+1:n]
(2)(2)(2) 那么问题就转变成了求 S[n−i+1:n]S[n-i+1: n]S[n−i+1:n] 和 S[1:n]S[1:n]S[1:n] 的最长公共前缀。
(3)(3)(3) 首先我们需要回到一个概念,叫字符串哈希,具体可以参见如下:
对于一个字符串 sss, s[l:r]s[l:r]s[l:r] 代表 sss 从 lll 到 rrr 的子串;
hash(s[1:1])=(s[1]∗B0)modPhash(s[1:1]) = ( s[1]*B^{0} ) \mod Phash(s[1:1])=(s[1]∗B0)modP
hash(s[1:2])=(s[1]∗B1+s[2]∗B0)modPhash(s[1:2]) = ( s[1]*B^{1} + s[2]*B^{0} ) \mod Phash(s[1:2])=(s[1]∗B1+s[2]∗B0)modP
hash(s[1:3])=(s[1]∗B2+s[2]∗B1+s[3]∗B0)modPhash(s[1:3]) = ( s[1]*B^{2} + s[2]*B^{1} + s[3]*B^{0}) \mod Phash(s[1:3])=(s[1]∗B2+s[2]∗B1+s[3]∗B0)modP
hash(s[1:4])=(s[1]∗B3+s[2]∗B2+s[3]∗B1+s[4]∗B0)modPhash(s[1:4]) = ( s[1]*B^{3} + s[2]*B^{2} + s[3]*B^{1} + s[4]*B^{0}) \mod Phash(s[1:4])=(s[1]∗B3+s[2]∗B2+s[3]∗B1+s[4]∗B0)modP
hash(s[1:5])=(s[1]∗B4+s[2]∗B3+s[3]∗B2+s[4]∗B1+s[5]∗B0)modPhash(s[1:5]) = ( s[1]*B^{4} + s[2]*B^{3} + s[3]*B^{2} + s[4]*B^{1} + s[5]*B^{0}) \mod Phash(s[1:5])=(s[1]∗B4+s[2]∗B3+s[3]∗B2+s[4]∗B1+s[5]∗B0)modP
那么我们如何求 hash(s[3:5])hash(s[3:5])hash(s[3:5]) 呢?
直接对字符串遍历,得到的结果为 hash(s[3:5])=(s[3]∗B2+s[4]∗B1+s[5]∗B0)modPhash(s[3:5]) = ( s[3]*B^{2} + s[4]*B^{1} + s[5]*B^{0}) \mod Phash(s[3:5])=(s[3]∗B2+s[4]∗B1+s[5]∗B0)modP,那么通过如下减法,得到:
hash(s[1:5])−hash(s[3:5])=(s[1]∗B4+s[2]∗B3)modP=B3∗(s[1]∗B1+s[2]∗B0)modP=B3∗hash(s[1:2])modP\begin{aligned}hash(s[1:5]) - hash(s[3:5]) &= ( s[1]*B^{4} + s[2]*B^{3} ) \mod P \\ &= B^3 * ( s[1]*B^{1} + s[2]*B^{0} ) \mod P \\ &= B^3 * hash(s[1:2]) \mod P \end{aligned}hash(s[1:5])−hash(s[3:5])=(s[1]∗B4+s[2]∗B3)modP=B3∗(s[1]∗B1+s[2]∗B0)modP=B3∗hash(s[1:2])modP
移项后整理式子,得到:
hash(s[3:5])=(hash(s[1:5])−B3∗hash(s[1:2]))modPhash(s[3:5]) = ( hash(s[1:5]) - B^3 * hash(s[1:2]) ) \mod Phash(s[3:5])=(hash(s[1:5])−B3∗hash(s[1:2]))modP
那么对于更加一般的情况,令 h(r)=hash(s[1:r])h(r) = hash(s[1:r])h(r)=hash(s[1:r]),有:
hash(s[l:r])=(h(r)−Br−l+1∗h(l−1))modPhash(s[l:r]) = ( h(r) - B^{r-l+1} * h(l-1) ) \mod Phash(s[l:r])=(h(r)−Br−l+1∗h(l−1))modP
其中 h(i)h(i)h(i) 和 BiB^iBi 都可以事先一次线性扫描预处理后放在数组中,则每次取子串哈希值的时间复杂度为 O(1)O(1)O(1)。
对于字符串哈希更加深入的内容,可以参考:夜深人静写算法(九)- 哈希表。
(4)(4)(4) 字符串哈希的模板如下:
#define ull unsigned long long
#define maxn 100010
#define P 10207
ull h[maxn], p[maxn];void initHash(const char *s) {// 这个字符串是从 1 - n-1int n = strlen(s);int i;p[0] = 1;h[0] = 0;for(i = 1; i < n; ++i) {h[i] = h[i-1] * P + s[i];p[i] = p[i-1] * P;}
}ull getSubHash(int l, int r) {return h[r] - h[l-1] * p[r - l + 1];
}
(5)(5)(5) 于是,对于每个位置 iii,只要二分长度,然后通过字符串哈希求出最长的公共子串,累加长度就是答案了。
2、时间复杂度
最坏时间复杂度 O(nlogn)O(nlogn)O(nlogn) 。
3、代码详解
#define ull unsigned long long
#define maxn 100010
#define P 10207
ull h[maxn], p[maxn];void initHash(const char *s) {// 这个字符串是从 1 - n-1int n = strlen(s);int i;p[0] = 1;h[0] = 0;for(i = 1; i < n; ++i) {h[i] = h[i-1] * P + s[i];p[i] = p[i-1] * P;}
}ull getSubHash(int l, int r) {return h[r] - h[l-1] * p[r - l + 1];
}class Solution {public:long long sumScores(string s) {s = "#" + s;long long ret = 0;int n = s.size() - 1;initHash(s.c_str());for(int i = 1; i <= n; ++i) {int l1 = n - i + 1;int l2 = 1;int l = 1, r = i;int ans = 0;while(l <= r) {int mid = (l + r) >> 1;if(getSubHash(l1, l1 + mid - 1) == getSubHash(l2, l2 + mid - 1)) {ans = mid;l = mid + 1;}else {r = mid - 1;}}ret += ans;}return ret;}
};
三、本题小知识
字符串哈希的问题,可以解决很多比较难想的字符串问题(代替 KMP、后缀树组 等等)。
四、加群须知
相信看我文章的大多数都是「 大学生 」,能上大学的都是「 精英 」,那么我们自然要「 精益求精 」,如果你还是「 大一 」,那么太好了,你拥有大把时间,当然你可以选择「 刷剧 」,然而,「 学好算法 」,三年后的你自然「 不能同日而语 」。
那么这里,我整理了「 几十个基础算法 」 的分类,点击开启:
leetcode 1400. 构造 K 个回文字符串 文章目录 leetcode 1400. 构造 K 个回文字符串 一.题目 1.题目描述 2.基础框架 3.解题思路 一.题目 原题链接:1400. ... LeetCode [344. 反转字符串] 题目:编写一个函数,其作用是将输入的字符串反转过来.输入字符串以字符数组 s 的形式给出. 不要给另外的数组分配额外的空间,你必须**原地修改输入数组**. ... 题目英文 Given two non-negative integers num1 and num2 represented as strings, return the product of num ... 题目英文 Given two non-negative integers num1 and num2 represented as string, return the sum of num1 and ... leetcode--344. 反转字符串 问题概述: 编写一个函数,其作用是将输入的字符串反转过来.输入字符串以字符数组 char[] 的形式给出. 不要给另外的数组分配额外的空间,你必须原地修改输入 ... 目录 leetcode 344.反转字符串 1.题目 2.思考 leetcode 541. 反转字符串 II 1.题目 2.思考 leetcode 344.反转字符串 1.题目 2.思考 典型的双指针 ... 文章目录 一.提出问题 二.解决问题 (一)分析 (二)编程 1.构造[1, n]范围内的斐波拉契数列表 2.基于斐波拉契数列构造字符串函数 3.编写主控程序 4.运行程序,查看结果 三.优化算法 一 ... 版权声明: 本文为博主Bravo Yeung(知乎UserName同名)的原创文章,欲转载请先私信获博主允许,转载时请附上网址 http://blog.csdn.net/lzuacm. Leetcod ... LeetCode--1849. 将字符串拆分为递减的连续值[Splitting a String Into Descending Consecutive Values][中等]--分析及代码[Java ...LeetCode 6036. 构造字符串的总得分和相关推荐
最新文章
热门文章