算法日常训练11.17

 求子序列,没什么想法,先用暴力走一波,不出意外应该是超时的,结果也确实不出意外,用动态规划?数组的字符串之间并没有什么规律,又不是求递增子序列那种题,应该想办法优化一下暴力算法,首先暴力超时是因为我们遍历数组,然后遍历里面的字符串,每一个字符串又要遍历s字符串,这样时间复杂度就是O(s*s1*words)(分别为字符串的长度和数组的长度),如果我们可以每次不需要浏览某个值,应该就可以优化算法,但是数组里的值都是不一样的,数组应该一定要遍历,而要求里面的字符串是否是s的子串,words[i]应该也需要遍历,而s每次来一个字符串他就被遍历了一遍,如果可以只遍历一遍s来判断words所有的字符串,算法就得到了优化,那么怎么操作呢?对我没想到,还是要看别的操作

 

class Solution {
    public int numMatchingSubseq(String s, String[] words) {
        Queue<int[]>[] queue=new LinkedList[26];//散列记录字母位置
        for(int i=0;i<26;i++){
            queue[i]=new LinkedList<>();//初始化队列数组
        }
        for(int i=0;i<words.length;i++){
            char c=words[i].charAt(0);//当前字符串的首字母
            queue[c-'a'].offer(new int[]{i,0});//i代表字符串在数组的位置,0代表字符串的首字母
        }
        int res=0;//保存结果
        for(int i=0;i<s.length();i++){
            char c=s.charAt(i);//取出当前字符
            int len=queue[c-'a'].size();//以当前字母开头的都要拿出来,代表他们和s当前字符相同,可以向下走了
            while(len>0){
                int[] arr=queue[c-'a'].poll();//一个一个取出来
                arr[1]++;//可以向后走一个字符了
                if(arr[1]>=words[arr[0]].length()){//走到了末尾,代表他是s的子串
                    res++;
                }
                else{//还没走完
                    char c1=words[arr[0]].charAt(arr[1]);//新字符
                    queue[c1-'a'].offer(new int[]{arr[0],arr[1]});//踹到新队列去
                }
                len--;
            }
        }
        return res;
    }
}

理解一下写起来还是比较简单的,主要理解了queue的作用写起来的时候就得心应手了,queue数组下标代表字母,保存的int[]数组,第一个保存字符串在words的位置,第二个保存当前走到字符串的哪个位置,这样一个queue,又保存了字符串是哪个,位置到哪里以及字符是什么,这样在遍历一次s时就可以把三个值全维护了,就可以实现遍历一次s判断所有字符串了。


 典型的滑动窗口问题,也是刚入力扣搞的题目,求子序列子数组这种问题,都可以先想一想滑动数组能不能用,滑动数组其实就是双指针,一边左窗口边界和右窗口,维护这两个这种就是滑动窗口。

class Solution {
    public int lengthOfLongestSubstring(String s) {
        if(s.length()==0||s.length()==1) return s.length();
        char[] arr=s.toCharArray();
        int left=0;
        int right=1;
        int[] pre=new int[130];
        pre[arr[0]]=1;
        int max=0;
        while(right<arr.length){
            
            if(pre[arr[right]]>=left){
                left=pre[arr[right]];
                
            }
            pre[arr[right]]=right+1;
            max=Math.max(max,right-left+1);
           
            right++;
        }
        return max;
    }
}

 


 很神奇的一个题目,最大值就是峰值,想ac直接找最大值就好了,我以为这已经是最好的偷懒方式了,直到看到。。。

 

 有脑子的人真帅,也不知道我什么时候能长脑子。不过这样也是O(n),但是可以在遍历完之前返回,不过题目O(logn),看到这个就知道想要的是二分法,这里的二分法有点玄学,一般二分法是要有规律的,这里也直接套用二分法模板,能出来,不过二分法就是把有正确数据的数组不断二分,这里只要让left和right不断爬坡,爬到他们相等的时候就代表到了顶峰。结合函数图思考一下就很容易理解,整个思想是取中点,如果他比他右边的数大,代表他在下坡或者峰值上,就让right压过去,不然就代表在上坡,就让left压过去,爬到最后left和right就相等了,就代表是峰值。

 

class Solution {
    public int findPeakElement(int[] nums) {
       int left = 0;
       int right = nums.length-1;
       while(left<right){ 
           int mid=(right-left)/2+left; 
           if(nums[mid]>nums[mid+1]){
               right = mid;
           }
           else{
               left = mid+1;
           }
       }
       return left;
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值