LeetCode 1147. Longest Chunked Palindrome Decomposition【贪心,双指针,字符串,动态规划,滚动哈希】困难

本文属于「征服LeetCode」系列文章之一,这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁,本系列将至少持续到刷完所有无锁题之日为止;由于LeetCode还在不断地创建新题,本系列的终止日期可能是永远。在这一系列刷题文章中,我不仅会讲解多种解题思路及其优化,还会用多种编程语言实现题解,涉及到通用解法时更将归纳总结出相应的算法模板。

为了方便在PC上运行调试、分享代码文件,我还建立了相关的仓库。在这一仓库中,你不仅可以看到LeetCode原题链接、题解代码、题解文章链接、同类题目归纳、通用解法总结等,还可以看到原题出现频率和相关企业等重要信息。如果有其他优选题解,还可以一同分享给他人。

由于本系列文章的内容随时可能发生更新变动,欢迎关注和收藏征服LeetCode系列文章目录一文以作备忘。

相关公司:小红书、谷歌 Google、彭博 Bloomberg、微软 Microsoft、亚马逊、字节跳动、J.P Morgan 摩根大通、优步 Uber

You are given a string text. You should split it to k substrings (subtext1, subtext2, ..., subtextk) such that:

  • subtexti is a non-empty string.
  • The concatenation of all the substrings is equal to text (i.e., subtext1 + subtext2 + ... + subtextk == text).
  • subtexti == subtextk - i + 1 for all valid values of i (i.e., 1 <= i <= k).

Return the largest possible value of k.

Example 1:

Input: text = "ghiabcdefhelloadamhelloabcdefghi"
Output: 7
Explanation: We can split the string on "(ghi)(abcdef)(hello)(adam)(hello)(abcdef)(ghi)".

Example 2:

Input: text = "merchant"
Output: 1
Explanation: We can split the string on "(merchant)".

Example 3:

Input: text = "antaprezatepzapreanta"
Output: 11
Explanation: We can split the string on "(a)(nt)(a)(pre)(za)(tep)(za)(pre)(a)(nt)(a)".

Constraints:

  • 1 <= text.length <= 1000
  • text consists only of lowercase English characters.

题意:你得到一个字符串 text 。应该把它分成 k 个子字符串 (subtext1, subtext2,…, subtextk) ,要求满足:

  • subtexti 是 非空 字符串
  • 所有子字符串的连接等于 text ( 即subtext1 + subtext2 + ... + subtextk == text )
  • 对于所有 i 的有效值( 即 1 <= i <= k ) ,subtexti == subtextk - i + 1 均成立

返回 k 可能最大值。


解法1 贪心+双指针

解决段式回文问题,我们可以设置两个指针 left, right 分别指向 text 的首尾、作为前缀子串的开始指针、后缀子串的结束指针。然后令 l = left, r = right ,再不断将 r 指针从后往前移动,一旦碰到 text[r] == text[l] ,就说明可能碰到相等的前后缀子串。

我们在里面再用一重循环,令 i = l, j = r ,将从 l 开始的前缀和从 r 开始的后缀匹配:

  • 如果有一处字符不同、或者 j 没有遍历到后缀 right 处,说明不是相等的前后缀子串,需要跳出最里面的循环,并 --r ,继续寻找前面的相等字符。这种情况下,如果始终没有找到相等的前后缀子串,就会让 l == r ,说明中间只有这一个子串,作为段式回文的中心,我们令 ans++ ,就返回 ans 作为结果。
  • 如果最里层的循环结束后,发现前后缀子串相等(此时 j > right,就令答案 ans += 2 ,并后移 left = i 、前移 right = r - 1 ,跳出上一层循环,并接着寻找相等的前后缀子串。这种情况下 l 不会等于 r
class Solution {
    public int longestDecomposition(String text) {
        int ans = 0;
        int left = 0, right = text.length() - 1;
        while (left <= right) {
            int l = left, r = right;
            while (l < r) { // 扫描从后往前的相同字符
                if (text.charAt(l) != text.charAt(r)) --r; // 继续往前找,text[l]==text[r]
                else {
                    int i = l, j = r; // [i,...] 和 [j,...]比较
                    boolean flag = true;
                    for (; i < r && j <= right; ++i, ++j) {
                        if (text.charAt(i) != text.charAt(j)) {
                            flag = false;
                            break;
                        }
                    }
                    if (flag && j > right) {
                        ans += 2;
                        left = i;
                        right = r - 1;
                        break; // 这里出来的l一定不等于r
                    } else --r; // 接着找上一个text[r]==text[l]
                }
            }
            if (l == r) { // 说明没找到一个text[r]==text[l],这就是一个字符串
                ++ans;
                break;
            }
        }
        return ans;
    }
}

复杂度分析:

  • 时间复杂度: O ( n 2 ) O(n^2) O(n2)
  • 空间复杂度: O ( 1 ) O(1) O(1)

解法2 贪心+枚举前后缀子串长度(递归/迭代)

我们不直接使用双指针,而是枚举前后缀长度 1 , 2 , 3 , … 1, 2, 3,\dots 1,2,3, ,和上种解法一样,一旦找到相同的前后缀,就直接切分为子串

(abc)zabc ... abcz(abc)

为什么不继续寻找?这就是证明贪心选择性质正确的地方:如果已经找到短的相同前后缀,对于更长的相同前后缀,它一定能分出更多的子串。如下所示,短的满足1=4,长的满足1=3且2=4,则2=3。

abczabc ... abczabc
(abc)(z)(abc) ... (abc)(z)(abc)
  1       2         3       4              

不过还有疑问:「短的前/后缀子串」的长度会超过「长的前/后缀子串」长度的一半吗?如果会这样的话,我们不可能把字符串分为两段!

这是不可能的。反证法证明:假设短的长度超过了长的长度的一半,则上面的证明还可得到:1=2,结合前面的假设,说明1和2有重叠,重叠部分既是短前缀的前缀,也是其后缀。

这么说很抽象,举个例子:ababa ... ababa 中1=2= aba ,重叠部分为 a ,既是 aba 的前缀,也是 aba 的后缀,这说明短的 aba 还可继续分割出更短的子串 a ,而不是作为一个无法分割的整体,矛盾。所以不会出现短的长度超过长的长度的一半的情况。

代码如下,可以递归求解:

class Solution {
public:
	int longestDecomposition(string s) {
		if (s.empty()) return 0;
		for (int i = 1, n = s.length(); i <= n / 2; ++i) // 枚举前后缀长度
			if (s.substr(0, i) == s.substr(n - i)) // 立刻分割
				return 2 + longestDecomposition(s.substr(i, n - i * 2));
		return 1; // 无法分割
	}
}

迭代形式如下:

class Solution {
public:
	int longestDecomposition(string s) {
		int ans = 0;
		while (!s.empty()) {
			int i = 1, n = s.length();
			while (i <= n / 2 && s.substr(0, i) != s.substr(n - i)) // 枚举前后缀
				++i;
			if (i > n / 2) { // 无法分割
				++ans;
				break;
			}
			ans += 2; // 分割出s[:i]和s[n-i:]
			s = s.substr(i, n - i * 2);
		}
		return ans;
	}
};

复杂度分析:

  • 时间复杂度: O ( n 2 ) O(n^2) O(n2) 。最坏情况下无法分割,需要执行 O ( n ) O(n) O(n) 次长为 O ( n ) O(n) O(n) 的字符串比较,所以时间复杂度为 O ( n 2 ) O(n^2) O(n2)
  • 空间复杂度: O ( n ) O(n) O(n)

解法3 贪心+字符串哈希+滚动哈希

只要选定合适的基数、令不同字符串的哈希值不同,就可对字符串进行哈希。相等的前/后缀子串计算得到的哈希值也相等,一旦发现相等的哈希值,就立刻分割子串(贪心),并重置已得的哈希值。

如下所示,设基数为 x ,分别从1的第一个字符 a 和4的最后一个字符 c 开始滚动哈希,得到1和4的哈希值后发现二者相等,则答案+2:

(abc)(z)(abc) ... (abc)(z)(abc)
  1       2         3       4
1: abc=a*x^2+b*x+c=((0*x+a)*x+b)*x+c

代码如下:

class Solution { 
	int base = 131;
	public int longestDecomposition(String s) {
		int n = s.length(), halfLen = n / 2;
		int h1 = 0, h2 = 0, temp = 1;
		int ans = 0;
		int maxi = 0;
		for (int i = 1; i <= halfLen ; ++i) {
			h1 = h1 * base + s.charAt(i - 1);
			h2 = h2 + s.charA(n - i) * temp;
			temp = temp * base;
			if (h1 == h2) {
				ans += 2;
				temp = 1;
				h1 = 0;
				h2 = 0;
				maxi = i;
			}
		}
		if (maxi == halfLen) ans = halfLen * 2 < halfLen ? ans + 1 : ans; // 到达的最大长度
		else ++ans; // 无法分割到中间,ans++
		return ans;
	}
}

复杂度分析:

  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( 1 ) O(1) O(1)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/11872.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

[Java]面向对象高级篇

文章目录包装类包装类层次结构基本类型包装类特殊包装类数组一维数组多维数组可变长参数字符串String类StringBuilder类内部类成员内部类静态内部类局部内部类匿名内部类Lambda表达式方法引用异常机制自定义异常抛出异常异常的处理常用工具类数学工具类随机数数组工具类包装类 …

在线文章生成工具-原创文章生成工具

在线文章生成器 在线文章生成器是指一种可以在线使用的自动化创造文章的工具。它可以使用自然语言处理&#xff08;NLP&#xff09;技术和人工智能算法提供需要的信息&#xff0c;基于标题、关键字&#xff0c;句子关联性等元素自动创造文章内容&#xff0c;涵盖各种类型&…

Java中线程的常用操作-后台线程、自定义线程工厂ThreadFactpry、join加入一个线程、线程异常捕获

场景 Java中Thread类的常用API以及使用示例&#xff1a; Java中Thread类的常用API以及使用示例_霸道流氓气质的博客-CSDN博客 上面讲了Thread的常用API&#xff0c;下面记录下线程的一些常用操作。 注&#xff1a; 博客&#xff1a;霸道流氓气质的博客_CSDN博客-C#,架构之…

Win10,详细永久关闭更新方法(附图文)

一、服务设置 1.同时按下键盘 Win R&#xff0c;打开运行对话框&#xff0c;然后输入命令 services.msc &#xff0c;点击下方的“确定”打开服务。 2.找到 Windows Update 这一项&#xff0c;并双击打开。 3.停止该服务&#xff0c;启动类型设置为禁用 4.点击恢复&#…

完整指南:如何安装Man手册

Man手册简介 man手册是Unix和类Unix操作系统中的命令行工具&#xff0c;用于提供关于特定命令、函数和文件的帮助文档。它通常包含命令的语法、选项、参数、示例以及其他相关信息。man手册可以通过在终端输入"man"命令&#xff0c;后跟要查看的命令或函数名称来访问…

惠普Probook455电脑开机突然卡住无法进入桌面

惠普Probook455电脑开机突然卡住无法进入桌面解决方法分享。最近有用户使用的惠普Probook455电脑在开机的时候&#xff0c;电脑一直卡在开机的界面上&#xff0c;无法进入到系统中。无论是重启还是安全模式都无法解决问题。那么遇到这个情况怎么去进行问题的解决&#xff0c;来…

远程组态管理的好处

远程组态管理可以简化管理工作&#xff0c;帮助您节省时间和金钱。远程组态管理可以通过各种应用程序来实现&#xff0c;包括&#xff1a; •监控所有设备的状态&#xff0c;以确保它们正常工作。 •记录现场数据&#xff0c;例如温度&#xff0c;压力或流量。 •快速、轻松地…

CSDN粉丝首破一千关,有你名字

2023-4-11&#xff0c;CSDN粉丝首破一千关。 感谢词版本1,哈哈哈哈哈哈哈哈 在编程世界里&#xff0c;人们可以像创造生命一样创造程序&#xff0c;而我对这种创造和创新的热情&#xff0c;从我的csdn博客社区粉丝首次突破一千人的消息中得到了极大的满足和激励。作为一个Pyth…

全面解析反欺诈(羊毛盾)API,助你识别各类欺诈风险

前言 反欺诈&#xff08;羊毛盾&#xff09;反机器欺诈 API&#xff0c;是一种基于大数据分析和模型产品的技术&#xff0c;通过输入手机号、手机 IP 地址进行检测&#xff0c;帮助客户识别大量存在恶意的账号。 反欺诈&#xff08;羊毛盾&#xff09;API 的作用 反欺诈&…

智慧工厂可视化合集,推动行业数字化转型

图扑软件基于 HTML5&#xff08;Canvas/WebGL/WebVR&#xff09;标准的 Web 技术&#xff0c;满足了工业物联网跨平台云端化部署实施的需求&#xff0c;以低代码的形式自由构建三维数字孪生、大屏可视化、工业组态等等。从 SDK 组件库&#xff0c;到 2D 和 3D 编辑&#xff0c;…

【Camunda】 -- Docker 安裝及使用

【Camunda】 -- Docker 安裝及使用1. Docker install Camunda platform1.1 Web2. Big Data -- Postgres1.1 Big Data -- Postgres3.Awakening1.1 Big Data -- PostgresCamunda platform 是一個任務監控的平台。 Camunda Modeler是建模工具。 1. Docker install Camunda platfor…

SpringSecurity之基础认知

前言 之前一直说开一个SpringSecurity的专栏&#xff0c;今天抽空整理一下&#xff0c;准备开始更新。 也欢迎大家订阅此专栏&#xff01; 什么是SpringSecurity&#xff1f; Spring是非常成功的Java应用框架&#xff0c;目前是非常主流的开发框架。Spring Securtiy正是我们…

基于K-最近邻算法构建红酒分类模型

基于K-最近邻算法构建红酒分类模型 描述 Wine红酒数据集是机器学习中一个经典的分类数据集&#xff0c;它是意大利同一地区种植的葡萄酒化学分析的结果&#xff0c;这些葡萄酒来自三个不同的品种。数据集中含有178个样本&#xff0c;分别属于三个已知品种&#xff0c;每个样本…

移动App测试实战—专项测试

移动App测试实战—专项测试 我们在进行了手工的功能测试之后&#xff0c;也开发了一些自动化测试用例&#xff0c;并且做了性能测试之后&#xff0c;测试工作看似比较完整了。但是当我们的App在大量的用户那里被安装和使用的时候&#xff0c;还是会有很多我们之前没有预料的问题…

微服务+springcloud+springcloud alibaba学习笔记【Hystrix(豪猪哥)的使用】(6/9)

Hystrix&#xff08;豪猪哥&#xff09;的使用 6/91、Hystrix熔断器概述2、HyStrix重要概念3、hystrix案例3.1 新建模块 Cloud-provider-hystrix-payment80013.2 创建带降级的order模块 Cloud-comsumer-feign-hystrix-order803.3 配置服务降级:3.3.1 服务降级 Cloud-provider-h…

3年功能测试无情被裁,3个月学习自动化测试重新开始........

前言 不知不觉在软件测试行业工作了3年之久&#xff0c;虽然说我是主做的功能测试&#xff0c;但是我也一直是兢兢业业的呀&#xff0c;不曾想去年7月份无情被辞的消息让我感到一阵沉重。我曾经一直坚信自己的技能和经验足以支撑我在这个领域的未来&#xff0c;但现实却告诉我&…

日撸 Java 三百行day31

文章目录day31 整数矩阵及其运算面向对象思想java异常处理java中的getter和setter方法代码day31 整数矩阵及其运算 面向对象思想 结合之前day7和day8面向过程开发&#xff0c;只关注了矩阵加法和矩阵乘法的功能。而day31是面向对象开发&#xff0c;一个矩阵类&#xff0c;在这…

傅盛“追风”GPT,猎户星空春天来了?

GPT的横空出世&#xff0c;让冷清已久的商用服务机器人市场&#xff0c;又有了“新故事”。 从技术底层逻辑而言&#xff0c;服务机器人受到这类新技术的影响会更为明显。因为抛开硬件&#xff0c;服务机器人的内核其实就是AI&#xff0c;GPT大模型的出现显然成了现阶段该产业进…

KDSL-82轻型升流器

一、产品概述 KDSL-82 1000A大电流发生器是一种作为检验用的电流源&#xff0c;大电流试验器采用ARM芯片控制输出工艺和大容量的环形变压器&#xff0c;并且配有液晶屏显示的表计&#xff0c;同时显示一、二次电流、变比和秒表接点(或电位)的动作时间。外配铝合金机箱&#xff…

Mybatis核心

文章目录前言一、Configuration二、MappedStatement三、SqlSession四、Executor五、StatementHandler六、ParameterHandler七、ResultSetHandler八、TypeHandler总结前言 SqlSession是MyBatis提供的面向用户的操作数据库API。那么MyBatis底层是如何工作的呢&#xff1f;为了解…