字符串轮转
给定两个字符串s1和s2,请编写代码检查s2是否为s1旋转而成
(比如,waterbottle是erbottlewat旋转后的字符串)。
示例1:
输入:s1 = “waterbottle”, s2 = “erbottlewat”
输出:True
示例2:
输入:s1 = “aa”, s2 = “aba”
输出:False
提示:
字符串长度在[0, 100000]范围内。
说明:
你能只调用一次检查子串的方法吗?
思路
这道题的意思是,给定一个字符串a,让字符串a的前面某一部分顺序放到后面,组成新的字符串b
既然是旋转(轮转),那么,新旧字符串的长度肯定不变,这也难怪示例2返回的是false
先判断两个字符串的长度是否相等,不相等返回false;相等,则进入下一步。
能否使用哈希表?
不能,虽然哈希表可以确认两个字符串是否拥有相同字符的个数,但是不能确定相互之间的顺序
那,是否可以将原来字符串分割为两部分,一部分为被旋转的a,一部分为没有被旋转的b
也就是ab与新的字符串ba做对比
如果能找到b串的开头,则拿b串的开头与ba做对比,b串比较完毕后,拿a进行剩余的对比
这个想法想起了之前判断两个链表的公共部分:相交链表
当时用的是一部分拼接到另外一部分的尾部
那么,这道题是否可以使用呢?
以s1 = “waterbottle”, s2 = "erbottlewat"为例
判断s2是否为s1的轮转,可以做两个s2:s2s2 = erbottlewaterbottlewat
在里面寻找,是否有s1(waterbottle)关键字
题目转化为,在一个字符串里面s2s2,查找是否存在s1
class Solution {
public boolean isFlipedString(String s1, String s2) {
if(s1 == null || s2 == null) return false;
if(s1.length() != s2.length()) return false;
return (s1 + s1).contains(s2);
}
}
代码简单的有点过分。。。
有没有可能,本来s2不是s1的轮转,而s2s2一拼接,在s2s2里面找到了s1的子串?
不会
假如s1是abc,那么s1+s1 = abcabc
里面的三位数的串为:
abc bca cab abc
只要在s1+s1里面找到了与s2相等的串,那么,s2就一定是s1的轮转
另一个树的子树
给定两个非空二叉树 s 和 t,检验 s 中是否包含和 t 具有相同结构和节点值的子树。s 的一个子树包括 s 的一个节点和这个节点的所有子孙。s 也可以看做它自身的一棵子树。
示例 1:
给定的树 s:
3
/ \
4 5
/ \
1 2
给定的树 t:
4
/ \
1 2
返回 true,因为 t 与 s 的一个子树拥有相同的结构和节点值。
示例 2:
给定的树 s:
3
/ \
4 5
/ \
1 2
/
0
给定的树 t:
4
/ \
1 2
返回 false。
思路
也就是,求t是否为s的子树,并且,t不能只是s子树的一部分
将二叉树的问题,转化为字符串的问题
使用层序遍历肯定是不行的,排除层序遍历
使用前、中、后序遍历树s,获取s的序列
再同样使用前、中、后序遍历树t,获取t的序列
如果t为s的子树,那么,t的序列为s的子序列
例子1中:
s的前序遍历:3->4->1->2->5
s的中序遍历:1->4->2->3->5
s的后序遍历:1->2->4->5->3
例子2中:
s的前序遍历:3->4->1->2->0->5
s的中序遍历:1->4->0->2->3->5
s的后序遍历:1->0->2->4->5->3
例子1和2中:
t的前序遍历:4->1->2
t的中序遍历:1->4->2
t的后序遍历:1->2->4
但,有一个问题,问题在于,只用一个前、中、后序,不能确定一个树的形状
我们知道,只有前、中 或者 中、后序遍历,两个一起能确定一棵树的形状。
那么,我们可以将空节点使用特殊符号 # 填充
另外,我们还需要特殊字符 -> 将节点之间进行标识
比如,你写123,是没法知道12是一个节点,还是1是一个节点
我们使用前序遍历,再次遍历例子1:
s的前序遍历:3->4->1->#->#->2->#->#->5->#->#->
t的前序遍历:4->1->#->#->2->#->#->
我们使用前序遍历,再次遍历例子2:
s的前序遍历:3->4->1->#->#->2->0->#->#->#->5->#->#->
t的前序遍历:4->1->#->#->2->#->#->
由一个二叉树,使用字符串表示出来的这个过程被称为序列化
class Solution {
public boolean isSubtree(TreeNode s, TreeNode t) {
if(s == null || t == null) return false;
//序列
return preSerialize(s).contains(preSerialize(t));
}
private String preSerialize(TreeNode root){
StringBuilder sb = new StringBuilder();
preSerialize(root, sb);
return sb.toString();
}
private void preSerialize(TreeNode node, StringBuilder sb)
{
if(node.left == null){
sb.append("#->");
}else{
preSerialize(node.left, sb);
}
if(node.right == null){
sb.append("#->");
}else{
preSerialize(node.right, sb);
}
//后序遍历
sb.append(node.val).append("->");
}
}
以上为后序遍历
如果上面代码直接使用,修改为前序遍历,有个坑需要注意一下:
[12]与[2]
12->#->#->
2->#->#->
s包含t
因此,在使用前序遍历的时候,需要
StringBuilder sb = new StringBuilder("->");
也就是,在前面加一个特殊符号,这样
->12->#->#->
->2->#->#->
s就不包含t了
又试了下中序遍历,提交的时候,提示错误:
[3,4,5,1,2,null,null,0]
[4,1,2]
leetCode的数组是层序遍历
两棵树的形状:
3
/ \
4 5
/ \
1 2
/
0
4
/ \
1 2
s的中序遍历#->0->#->1->#->4->#->2->#->3->#->5->#->
t的中序遍历--------#->1->#->4->#->2->#->--------------
这么看来,中序遍历是不行的
为何中序遍历不行???
自己写的前序遍历解决问题,思想是一样的
class Solution {
public boolean isSubtree(TreeNode s, TreeNode t) {
if(s == null || t == null) return false;
return preTree(s).contains(preTree(t));
}
private String preTree(TreeNode node)
{
String s = new String();
if(node == null) {
return s + "#->";
}
s = s + preTree(node.left);
s = s + preTree(node.right);
//打印节点
s = s + node.val + "->";
return s;
}
}
这样也是对的