贪心 - 段式回文 - Leetcode 1147

贪心 - 段式回文 - Leetcode 1147

段式回文 其实与 一般回文 类似,只不过是最小的单位是 一段字符 而不是 单个字母。

举个例子,对于一般回文 “abcba” 是回文,而 “volvo” 不是,但如果我们把 “volvo” 分为 “vo”、“l”、“vo” 三段,则可以认为 “(vo)(l)(vo)” 是段式回文(分为 3 段)。

给你一个字符串 text,在确保它满足段式回文的前提下,请你返回 段 的 最大数量 k。

如果段的最大数量为 k,那么存在满足以下条件的 a1, a2, …, ak

  • 每个 a_i 都是一个非空字符串;
  • 将这些字符串首位相连的结果 a1 + a2 + … + ak 和原始字符串 text 相同;
  • 对于所有1 <= i <= k,都有 ai = ak+1-i

示例 1:

输入:text = "ghiabcdefhelloadamhelloabcdefghi"
输出:7
解释:我们可以把字符串拆分成 "(ghi)(abcdef)(hello)(adam)(hello)(abcdef)(ghi)"。

示例 2:

输入:text = "merchant"
输出:1
解释:我们可以把字符串拆分成 "(merchant)"。

示例 3:

输入:text = "antaprezatepzapreanta"
输出:11
解释:我们可以把字符串拆分成 "(a)(nt)(a)(pre)(za)(tpe)(za)(pre)(a)(nt)(a)"。

示例 4:

输入:text = "aaa"
输出:3
解释:我们可以把字符串拆分成 "(a)(a)(a)"。

提示:

text 仅由小写英文字符组成。
1 <= text.length <= 1000


分析:

策略: 从左至右扫描,优先选择最短的“段”进行匹配。(思考:这样是不是能够匹配最多的段?有没有可能前面匹配的段不是最短的,可以使得后面匹配的段更多?)

证明:

在这里插入图片描述

对于字符串 s [ 1 : n ] s[1:n] s[1:n],我们假设最左端(最短)的一个匹配的段在位置 p 1 p_1 p1,即 s [ 1 : p 1 ] = s [ n − p 1 + 1 : n ] 。 s[1:p_1]=s[n-p_1+1:n]。 s[1:p1]=s[np1+1:n]

假设我们在 p 1 p_1 p1 处不进行匹配,而在 p 2 p_2 p2 处匹配,即 s [ 1 : p 2 ] = s [ n − p 2 + 1 : n ] 。 s[1:p_2]=s[n-p_2+1:n]。 s[1:p2]=s[np2+1:n]

可以得到, s [ p 2 − p 1 + 1 : p 2 ] = s [ n − p 1 + 1 : n ] = s [ 1 : p 1 ] s[p_2-p_1+1:p_2]=s[n-p_1+1:n]=s[1:p_1] s[p2p1+1:p2]=s[np1+1:n]=s[1:p1]

进而得到, s [ 1 : p 2 − p 1 + 1 ] = s [ p 1 : p 2 ] s[1:p_2-p_1+1]=s[p_1:p_2] s[1:p2p1+1]=s[p1:p2],即红色段拥有一个长度为 L c o m = p 2 − p 1 + 1 L_{com}=p_2-p_1+1 Lcom=p2p1+1 相同前缀和后缀。

因此, s [ 1 : L c o m ] = s [ n − L c o m + 1 : n ] s[1:L_{com}]=s[n-L_{com}+1:n] s[1:Lcom]=s[nLcom+1:n]

这就与我们的假设:最左端的匹配段的位置是 p 1 p_1 p1 矛盾了。最左端的匹配的位置应该在 L c o m L_{com} Lcom 处。

所以我们应该每次匹配最短的段。

代码:

class Solution {
public:
int calc(string& text, int l, int r)
{
	int n = text.size();
	string a, b;
	for(int k = 1; k <= n >> 1; k ++)
	{
		a = text.substr(l, k);
		b = text.substr(r - k + 1, k);
		if(a == b) return k;
	}
	return 1000;
}

int longestDecomposition(string text) 
{
	int n = text.size();
	int ans = 0;
	int l = 0, r = n - 1;
	while(l <= r)
	{
		int k = calc(text, l, r);
		if(l + k - 1 < r - k + 1) ans += 2;
		else ++ ans;
		l += k;
		r -= k;	 
	}
	return ans;
}
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值