2018年LeetCode高频算法面试题刷题笔记——分割回文串(字符串)

1.解答之前的碎碎念:

这个题我的想法是:

第一刀依次切在第1——s.length() - 2个元素后面,得到两个字符串s0和s1。

首先判断s0整体是否为回文,不是则第一刀的位置+1。

然后再检测s1整体是否为回文,并在s1的第1——s1.length() - 2个元素后面切第二刀。

然后判断s10是否为回文,不是则第二刀的位置+1。

然后再检测s11整体是否为回文,并在s11的第1——s11.length() - 2个元素后面切第三刀。

 

以此类推。

 

然后,然后我就不知道代码怎么写了。。。

 

2.问题描述:

给定一个字符串 s,将 s 分割成一些子串,使每个子串都是回文串。

返回 s 所有可能的分割方案。

示例:

输入: "aab"
输出:
[
  ["aa","b"],
  ["a","a","b"]
]

3.解答思路:

深度优先搜索(DFS):https://blog.csdn.net/whdAlive/article/details/81254975(这个方法和我的想法有点像)

要点摘录:

解题思路就是 DFS,用一个boolean[] 数组记录当前可分割的位置,每一次深搜的过程都是找下一个可以分割的位置。

为了更容易理解,通过代码简单输出执行的流程(代码中 注释掉的输出部分),建议自己捋清楚整个算法的执行流程,以便举一反三,下面给出我整理的流程(只给思路,不涉及分割位置数组的细节)。

实际上对于 s=”aab” 而言,就是三层递归,每层递归向后找能分割的位置:

层1:从下标 0~2 寻找分割位置,对应子串为 aab
层2:从下标 x~2(x为 最外层传入的下标 和 1 中的大值) 寻找分割位置,对应子串为 ab 或 b
层3:从下标 y~2(y为 中间层传入的下标 和 2 中的大者 ) 寻找分割位置,对应子串为 b 或 无

也就是说,这个题建的树的每一层都对应第n次切的位置。第0层就是不切,所以树的根为原字符串“aab”,第一层对应的是只切一刀的结果,即“a”,"ab"和“aa”,"b"。每次递归都检测左边字符串是否为回文,若是,则对右边的字符串进行再切割。

上面那个链接的方法还是有点啰嗦,可以参考这个链接的代码:https://blog.csdn.net/weixin_41413441/article/details/80926957

 

4.相关知识:

递归、回溯和DFS的区别:https://blog.csdn.net/fengchi863/article/details/80086915

要点摘录:

  • 递归是一种算法结构,回溯是一种算法思想。
  • 一个递归就是在函数中调用函数本身来解决问题。
  • 回溯就是通过不同的尝试来生成问题的解,有点类似于穷举,但是和穷举不同的是回溯会“剪枝”。

剪枝的意思也就是说对已经知道错误的结果没必要再枚举接下来的答案了,比如一个有序数列1,2,3,4,5,我要找和为5的所有集合,从前往后搜索我选了1,然后2,然后选3的时候发现和已经大于预期,那么4,5肯定也不行,这就是一种对搜索过程的优化。

回溯搜索是深度优先搜索(DFS)的一种。对于某一个搜索树来说(搜索树是起记录路径和状态判断的作用),回溯和DFS,其主要的区别是,回溯法在求解过程中不保留完整的树结构,而深度优先搜索则记下完整的搜索树。

为了减少存储空间,在深度优先搜索中,用标志的方法记录访问过的状态,这种处理方法使得深度优先搜索法与回溯法没什么区别了。


5.答案:

class Solution {
public:
	vector<vector<string>> partition(string s) {
		vector<vector<string>> result;
        vector<string> tmp;
		cut(s, result, tmp, 0);
		return result;
	}

	void cut(string s, vector<vector<string>> &result, vector<string> tmp, int right_start) {
        if(right_start >= s.length())
        {
            result.push_back(tmp);
            return;
        }

		for (int i = right_start;i < s.length(); i++) //每次cut的可能位置
		{
			if (isPalindrome(s, right_start, i))
			{
				tmp.push_back(s.substr(right_start, i - right_start + 1));
				cut(s, result, tmp, i + 1);
                tmp.pop_back();//注意在第n刀切的位置改变时,要把之前存储的结果删除
            }
            
		}
        return;



	}
	bool isPalindrome(string s, int start, int end) {
		while (start <= end && s[start] == s[end])
		{
			start++;
			end--;
		}
		return start > end;
	}
};

6.类似题目:

leetcode 17题 —— 电话号码的数字组合:

题目:

给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。

给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。

示例:

输入:"23"
输出:["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"].

思路:

用输入的各个数字对应的字母建一颗树,每个字母都是一个节点,树根为“”。

则第一个输入的数字对应的字母为第一层的节点,第二个数字对应字母为第二层节点,以此类推。

进行DFS就可以得到问题的解。

解答:

class Solution {
public:
    vector<string> letterCombinations(string digits) {
        vector<string> s_all = {"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
        vector<string> s_result;
        if(digits.size() == 0) return s_result;
        combination(digits,s_all,s_result,"",0);
        return s_result;
    }
    void combination(string digits,vector<string> s_all,vector<string> &s_result,string tmp,int count)
    {
        if(count >= digits.size()) {s_result.push_back(tmp); return;}//一条线走到底的结果加入最终的结果
        int index = digits[count] - '0';
        for(int i = 0;i < s_all[index].size();i++) //开启一个分支
        {
            combination(digits,s_all,s_result,tmp+s_all[index][i],count + 1);
        }
        return;
    }
};

leetcode 93题 —— 复原IP地址:

题目:

给定一个只包含数字的字符串,复原它并返回所有可能的 IP 地址格式。

示例:

输入: "25525511135"
输出: ["255.255.11.135", "255.255.111.35"]

思路:

这个题的思路和上一个题很像,用的是回溯法(DFS的特殊情况,存在剪枝)

树的根节点为输入,第一层节点为ip地址中第一个“.”应该加的位置,且每一个“.”要加的位置只有当前子串第一个数字、第二个数字、第三个数字后三个选择。且分割出来的第n段ip地址满足小于256和剩余子串长度小于等于(4-n)*3(也就是剩余子串还符合构成ip地址的条件)。

注意:

“.001.”为非法ip地址。

解答:

class Solution {
public:
	vector<string> restoreIpAddresses(string s) {
		vector<string> result;
		vector<int> cut_point = { 0,0,0 };
		if (s.size() == 0) return result;
		restore(s, s, result, cut_point, 1);
		return result;
	}
	void restore(string s,string s_tmp, vector<string> &result, vector<int> cut_point, int count)
	{
		if (count > 4)
		{
			int i = 0;
			int c = 0;
			string r_tmp = "";
			while (i < s.size())
			{
				r_tmp += s[i];
				if (c < 3 && i == cut_point[c])
				{
					r_tmp += ".";
					c++;
				}
				i++;				
			}
			result.push_back(r_tmp);
			return;
		}
		for (int i = 0; i < 3; i++)
		{
			if (i >= s_tmp.size()) return;
			string tmp = "";
			for (int j = 0; j <= i; j++)
			{
				if (j > 0 && s_tmp[0] == '0') 
				{
					return;
				}
				tmp += s_tmp[j];
			}
			int num_tmp = atoi(tmp.c_str());
			if (num_tmp < 256 && ((s_tmp.size() - i - 1) <= (4 -  count) * 3))
			{
				if (count != 4)
				{
					if(count == 1)	cut_point[count - 1] = i;
					else cut_point[count - 1] = i + cut_point[count - 2] + 1;
				}
				
				string ss = "";
				for (int j = i + 1; j < s_tmp.size();j++)
				{
					ss += s_tmp[j];
				}
				restore(s, ss, result, cut_point, count + 1);
			}
		}
		return;
	}
};

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值