题目描述1
笔者解答1.1
class Solution {
public String shortestPalindrome(String s) {
int length=s.length();
for(int i=length-1;i>=0;i--)
{
for(int j=0;j<=i/2;j++)
{
if(s.charAt(j)!=s.charAt(i-j))
{
break;
}
if(j==i/2)
{
int n=2*s.length()-1-i;
char[] str=new char[n];
int k;
for(k=0;k<length-1-i;k++)
{
str[k]=s.charAt(length-k-1);
str[n-k-1]=s.charAt(length-k-1);
}
for(k=length-1-i;k<n;k++)
{
str[k]=s.charAt(k-length+1+i);
}
return String.valueOf(str);
}
}
}
return "";
}
}
笔者分析1.2
虽然自我感觉还是挺巧妙的,但有些条件下超时了,我的想法是,不断从原字符串末尾删除字符,直到剩余字符满足中心对称,然后再在头部加上被删除的字符拼接成新的字符,但缺点就是时间消耗太长了,之得到评论区向大佬求助,字符串哈希是真的牛!在该方法中,我们将字符串看成一个base进制的数,它对应的十进制就是哈希值。显然,两个字符串的哈希值相等,当且仅当这两个字符串本身相同,然而如果字符传本身很长,其对应的十进制在大多数语言中无法使用内置的整数类型进行存储。因此,我们会将十进制对一个大质数mod进行取摸。
一般来说,我们选取一个大于字符集大小(即字符串中可能出现的字符种类的数目)的质数作为base,再选取一个字符串长度平方级别左右的质数作为mod,产生哈希碰撞的概率就会很低。
算法
一个字符串是回文串,当且仅当该字符串与它的反序相同。因此,我们仍然暴力地美剧s的结束位置,并计算s与s反序的哈希值。如果这两个哈希值相等,说明我们找到了一个s的前缀回文串。设当前枚举到的结束位置为i,对应的s记为si,其反序记为si+.我们可以通过地推的方式,在O(1)的时间内通过s(i-1)和s(i-1)+的哈希只得到si和si+的哈希值:
hash(si)=hash(s(i-1)*base+ASCII(si[i])
hash(si+)=hash(s(i-1)+)+ASCII(s[i])*base;
class Solution {
public String shortestPalindrome(String s) {
int n = s.length();
int base = 131, mod = 1000000007;
int left = 0, right = 0, mul = 1;
int best = -1;
for (int i = 0; i < n; ++i) {
left = (int) (((long) left * base + s.charAt(i)) % mod);
right = (int) ((right + (long) mul * s.charAt(i)) % mod);
if (left == right) {
best = i;
}
mul = (int) ((long) mul * base % mod);
}
String add = (best == n - 1 ? "" : s.substring(best + 1));
StringBuffer ans = new StringBuffer(add).reverse();
ans.append(s);
return ans.toString();
}
}
题目描述2
笔者解答2.1
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> list=new ArrayList<Integer>();
inOrder(root,list);
return list;
}
public static void inOrder(TreeNode root,List<Integer> list)
{
if(root!=null)
{
if(root.left!=null)
inOrder(root.left,list);
list.add(root.val);
if(root.right!=null)
inOrder(root.right,list);
}
}
}
笔者分析2.2
呃,,题目说了用迭代的,忘了。递归的话也太简单了,所以这里着重介绍一下迭代,听巧妙的。只用一个栈就能完成二叉树的中序遍历。
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> list = new ArrayList<>();
Stack<TreeNode> stack = new Stack<>();
TreeNode cur = root;
while (cur != null || !stack.isEmpty()) {
if (cur != null) {
stack.push(cur);
cur = cur.left;
} else {
cur = stack.pop();
list.add(cur.val);
cur = cur.right;
}
}
return list;
}
}
题目描述3
笔者分析3.1
这么说吧,我没看懂题目意思。。。但看了下评论区的解答,手法之巧妙,让我即使没写出来,也想把这种手法记录下来。不是我说,栈这一数据结构也太牛逼了吧,以前还没怎么看重它,现在后悔了。二叉搜索树太适合用栈了。
class BSTIterator {
private Stack<TreeNode> stack;
public BSTIterator(TreeNode root) {
stack=new Stack<>();
while(root!=null)
{
stack.push(root);
root=root.left;
}
}
/** @return the next smallest number */
public int next() {
TreeNode node=stack.pop();
int result=node.val;
if(node.right!=null){
node=node.right;
while(node!=null){
stack.push(node);
node=node.left;
}
}
return result;
}
/** @return whether we have a next smallest number */
public boolean hasNext() {
if(stack.empty())
{
return false;
}
return true;
}
}
题目描述4
笔者解答4.1
class Solution {
public boolean isValidSerialization(String preorder) {
Stack<String> stack=new Stack<String>();
List<String> list=new ArrayList<String>();
list=get_String(preorder);
if(list==null||(list.get(0).equals("#")&&list.size()==1))
return true;
for(int i=0;i<list.size();i++){
String str=list.get(i);
if((i!=0&&stack.isEmpty())||(i==0&&str.equals("#")))
return false;
if(str.equals("#"))
{
while(stack.peek().equals("#")&&str.equals("#"))
{
stack.pop();
stack.pop();
str="#";
if(stack.isEmpty())break;
}
if(!stack.isEmpty())
stack.push("#");
}
else
stack.push(str);
}
if(stack.isEmpty())
return true;
return false;
}
public static List<String> get_String(String preorder){
List<String> list=new ArrayList<String>();
int getlength=0;
int last_position=0;
for(int i=0;i<preorder.length();i++){
if(preorder.charAt(i)!=',')
{
getlength++;
}
else
{
list.add(preorder.substring(last_position,last_position+getlength));
getlength=0;
last_position=i+1;
}
}
list.add(preorder.substring(last_position,last_position+getlength));
return list;
}
}
笔者分析4.2
思路感觉并不复杂,只不过有好多细节没注意到,经过不断调试,虽然最后通过了,但代码也就成了上面那副鬼样。我的思路是如果是数字则直接存入,如果连入两个#则将栈内数据弹出两个(必定是一个#,一个数字),再存入一个#,这里需要一个while循环,因为可能会有多米诺骨牌现象。最后若栈空则该二叉树成立,补充一些题目要求正确的格式就行了。但看到大佬们只用了几行代码就解决了,瞬间脸黑,原来题目还可以这样抽象的写。
迭代:我们可以定义一个概念,叫做槽位,二叉树中任意一个及诶单或者空孩子及诶单都要占据一个槽位。二叉树的建立也伴随这槽位数量的变化。开始时只有一个槽位,如果根节点是空节点,就只消耗掉一个槽位,如果根节点不是空节点,除了消耗一个槽位,还要为孩子节点增加两个新的槽位。之后的节点也是同理。
有了上面的讨论,方法就很简单了。依次遍历前序序列化,根据节点是否为空,按照规则消耗、增加槽位。如果最后可以将所有的槽位消耗完,那么这个前序序列化就是合法的。代码如下。
class Solution {
public boolean isValidSerialization(String preorder) {
// number of available slots
int slots = 1;
for(String node : preorder.split(",")) {
// one node takes one slot
--slots;
// no more slots available
if (slots < 0) return false;
// non-empty node creates two children slots
if (!node.equals("#")) slots += 2;
}
// all slots should be used up
return slots == 0;
}
}
总结
今天写的题目类型比较少,虽然写了十题,但比较有特点的也就这几题而已,每日十题打卡第二天,以下图为证。