LeetCode Day13 KMP+双指针

459 重复的子字符串

题目理解

判断给定字符串是否能由重复子串构成,重复子串,因此子串的长度一定小于字符串的长度/2。

思路

借鉴一下别人的思路,可以转变为在一个串中查找另一个串的问题,KMP可以有效解决这一问题。我们假设字符串s是重复的,s=aaaaa,其中a为最小的重复子串,则s+s=aaaaaaaaaa,此时s+s中必有两个s,我们从字符串的第二位开始匹配是否能找到s,就可以判断s是否由重复子串构成。如果s不重复,如s=abcd,则s+s=abcdabcd,当我们从第二位开始匹配时,从s+s中找到的s的首字符的位置一定是s+s中第二个s的位置,即s.size()(下标从0开始),而如果s是重复的呢,从第二位匹配,因此第一个a被破坏,找到的s是从第二个a开始,起始位置一定小于s.size(),这样就可以得出结论。

代码

class Solution {
public:
    void getNext(vector<int>& next,string s) {
        int i = 1,j = 0;  //i为后缀下标,j为前缀下标
        next[j] = j;
        for (;i < s.size();i++) {
            while (j > 0 && s[i] != s[j]) //匹配失败的是后缀子串的后面,我们就要从与后缀子串相同的前缀子串的下一位开始比较。
                j = next [j-1];
            if(s[i] == s[j]) {
                ++j;
            }
            next[i] = j;
        }
    }
    bool repeatedSubstringPattern(string s) {
        vector<int> next(s.size());
        string sum = s + s;
        getNext(next,s);
        int j = 0;
        for(int i = 1;i < sum.size();i++) {
            while (j > 0 && sum[i] != s[j])
                j = next[j - 1];
            if(sum[i] == s[j])
                ++j;
            if (j == s.size() && i != sum.size() - 1) {
                    return true;
            }
        }
        return false;
    }
};

27 移除元素

题目理解

给定数组nums和值val,原地移除val,返回移除后的数组长度。

思路

简单题,只需要用快慢指针,慢指针记录移除后的数组元素下标,快指针没有遇到val时,快指针所指的值赋给慢指针,快慢指针++,遇到val时,慢指针不动,直到快指针遇到下一个不是val的值,赋给慢指针所指的数组元素,移除val后的数组长度即为慢指针的值。

代码

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int fast = 0,slow = 0;
        for(;fast < nums.size();fast++) {
            if (nums[fast] == val)
                continue;
            else
                nums[slow++] = nums[fast];//slow从0开始,因此长度为++后的结果。
        }
        return slow;
    }
};

18 四数之和

题目理解

从数组nums中找到和为target的四元组,且四元组不重复。

思路

和前面做过的三数之和的题思路相同。三数之和的求解思路是,先对数组排序,i从0开始遍历数组,固定i以后,使用双指针L和R分别表示 i 以后的元素的首位和末位,如果nums[i]、nums[L]、nums[R]的和大于target的话,因为数组是排过序的,所以R向左移,减小nums[R];如果和小于target的话,L向右移,增大nums[L],直到等于target,将此三元组加入结果数组中。

要注意的是怎样让求得的三元组不重复,i 从0开始自然不需要考虑,当i=1时,如果nums[1]和nums[0]相同,因为0以后的元素包含1以后的元素,自然最后求得的三元组是相同的,因此只要nums[i]=nums[i-1]时,就可以跳过;里层循环怎样不重复,就是在找到一个三元组后,会移动L和R,如果nums[L]=nums[L-1]或者nums[R]=nums[R-1],自然也要跳过。
四数之和的求解只需要在三数之和的基础上再加一层遍历即可。外层先固定两个数,再从剩余数中寻找。

代码

class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        int n = nums.size();
        if(n < 4)
            return {};
        vector<vector<int>> res;
        sort(nums.begin(),nums.end());
        for(int i = 0;i < n - 3;i++){
            if(i > 0 && nums[i] == nums[i - 1])
                continue;
            for(int j = i + 1;j < n - 2;j++) {
                if(j > i + 1 && nums[j] == nums[j - 1])
                    continue;
                int L = j + 1,R = n - 1;
                while(L < R) {
                    if(nums[i] + nums[j] +nums[L] + nums[R] < target)
                        ++L;
                    else if(nums[i] + nums[j] +nums[L] + nums[R] > target)
                        --R;
                    else {
                        res.push_back({nums[i],nums[j],nums[L],nums[R]});
                        while(L < R && nums[L] == nums[L+1])  ++L; //去重要在找到一个四元组以后
                        while(L < R && nums[R] == nums[R-1])  --R;
                        ++L; --R;
                    }
                }
            }
        }
        return res;   
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值