Leetcode_字符串string(3)28.32.38.

目录

LC_28.实现 strStr() *

LC_32. 最长有效括号  H*

38. 外观数列 E

 


重点看:28,32

没意思:38

LC_28.实现 strStr() *

方法一:子串逐一比较 - 线性时间复杂度  

最直接的方法 - 沿着主串逐步移动滑动窗口,将窗口内的子串与 needle 字符串比较。(窗口大小=子串长度)

方法二:双指针 - 线性时间复杂度

上一个方法的缺陷是会将 haystack 所有长度为 L 的子串都与 needle 字符串比较,实际上是不需要这么做的。

首先,只有子串的第一个字符跟 needle 字符串第一个字符相同的时候才需要比较

其次,可以一个字符一个字符比较,一旦不匹配了就立刻终止。

方法三: Rabin Karp - 常数复杂度
有一种最坏时间复杂度也为 O(N) 的算法。思路是这样的,先生成窗口内子串的哈希码,然后再跟 needle 字符串的哈希码做比较。

这个思路有一个问题需要解决,如何在常数时间生成子串的哈希码?

滚动哈希:常数时间生成哈希码

生成一个长度为 L 数组的哈希码,需要 O(L) 时间。

如何在常数时间生成滑动窗口数组的哈希码?利用滑动窗口的特性,每次滑动都有一个元素进,一个出。

由于只会出现小写的英文字母,因此可以将字符串转化成值为 0 到 25 的整数数组: arr[i] = (int)S.charAt(i) - (int)'a'。按照这种规则,abcd 整数数组形式就是 [0, 1, 2, 3],转换公式如下所示。

                                       h_{0}= 0*26×^{^{3}}+1*26×^{^{2}}+2*26×^{^{1}}+3*26×^{^{0}}          

可以将上面的公式写成通式,如下所示。其中 ci​ 为整数数组中的元素,a = 26,其为字符集的个数

                    

下面来考虑窗口从 abcd 滑动到 bcde 的情况。这时候整数形式数组从 [0, 1, 2, 3] 变成了 [1, 2, 3, 4],数组最左边的 0 被移除,同时最右边新添了 4。滑动后数组的哈希值可以根据滑动前数组的哈希值来计算,计算公式如下所示。                                            

                                          h^{_{1}}=(h^{_{0}}-0*26^{3})*26+4*26^{0}

 

如何避免溢出

a^L 可能是一个很大的数字,因此需要设置数值上限来避免溢出。设置数值上限可以用取模的方式,即用 h % modulus 来代替原本的哈希值。

理论上,modules 应该取一个很大数,但具体应该取多大的数呢? 详见这篇文章,对于这个问题来说 2^{31} 就足够了。

感觉理解起来还是很简单的,很实用啊 

 

 

LC_32. 最长有效括号  H*

暴力枚举偶数长度的子串用stack判断是否有效 

public class Solution {
    public boolean isValid(String s) {
        Stack<Character> stack = new Stack<Character>();
        for (int i = 0; i < s.length(); i++) {
            if (s.charAt(i) == '(') {
                stack.push('(');
            } else if (!stack.empty() && stack.peek() == '(') {
                stack.pop();
            } else {
                return false;
            }
        }
        return stack.empty();
    }
    public int longestValidParentheses(String s) {
        int maxlen = 0;
        for (int i = 0; i < s.length(); i++) {
            for (int j = i + 2; j <= s.length(); j+=2) {
                if (isValid(s.substring(i, j))) {
                    maxlen = Math.max(maxlen, j - i);
                }
            }
        }
        return maxlen;
    }
}

用stack模拟  O(n)

public class Solution {

    public int longestValidParentheses(String s) {
        int maxans = 0;
        Stack<Integer> stack = new Stack<>();
        stack.push(-1);
        for (int i = 0; i < s.length(); i++) {
            if (s.charAt(i) == '(') {
                stack.push(i);
            } else {
                stack.pop(); 
                if (stack.empty()) {
                    stack.push(i);
                } else {
                    maxans = Math.max(maxans, i - stack.peek());
                }
            }
        }
        return maxans;
    }
}

38. 外观数列 E

「外观数列」是一个整数序列,从数字 1 开始,序列中的每一项都是对前一项的描述。前五项如下:

1.     1
2.     11
3.     21
4.     1211
5.     111221
1 被读作  "one 1"  ("一个一") , 即 11。
11 被读作 "two 1s" ("两个一"), 即 21。
21 被读作 "one 2",  "one 1" ("一个二" ,  "一个一") , 即 1211。

给定一个正整数 n(1 ≤ n ≤ 30),输出外观数列的第 n 项。

注意:整数序列中的每一项将表示为一个字符串。

打表 ,n<=30

class Solution {
public:
    string countAndSay(int n) {
        int i=1;
        string s="1";
        while(i<n){
            string tmp="";
            int num=1;
            for(int j=1;j<s.length();j++){
                if(s[j-1]==s[j]){
                    num++;
                } else{
                    tmp+=to_string(num);
                    tmp+=s[j-1];
                    num=1;
                }
            }
            tmp+=to_string(num);//对出现在末尾的连续数字结果尚未保存
            tmp+=s[s.length()-1];
            s=tmp;
            i++;
        }
        return s;
    }
};

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值