打卡leetcode第14天

这篇博客探讨了三种不同的字符串匹配方法:暴力解法、利用strStr()和KMP算法,并分析了它们的时间效率。此外,还介绍了如何通过删除字母匹配字典中的最长单词,以及两种解决有序数组平方问题的策略:先平方后排序和双指针法。最后,讨论了如何使用双指针法有效地移动数组中的零元素。
摘要由CSDN通过智能技术生成

1.实现strStr(三种解法)

【题目】

实现 strStr() 函数。

给你两个字符串 haystack 和 needle ,请你在 haystack 字符串中找出 needle 字符串出现的第一个位置(下标从 0 开始)。如果不存在,则返回 -1 。

说明:

当 needle 是空字符串时,我们应当返回什么值呢?这是一个在面试中很好的问题。

对于本题而言,当 needle 是空字符串时我们应当返回 0 。这与 C 语言的 strstr() 以及 Java 的 indexOf() 定义相符。

【分析】

法1:暴力解法:

我们可以让字符串 needle 与字符串 haystack 的所有长度为 m 的子串均匹配一次。

为了减少不必要的匹配,我们每次匹配失败即立刻停止当前子串的匹配,对下一个子串继续匹配。如果当前子串匹配成功,我们返回当前子串的开始位置即可。如果所有子串都匹配失败,则返回 −1。

【C】

int strStr(char * haystack, char * needle){
    for(int i=0;i+strlen(needle)<=strlen(haystack);i++){
        bool flag=true;
        for(int j=0;j<strlen(needle);j++){
        if(haystack[i+j]!=needle[j]){
            flag=false;
            break;
        }
    }
    if(flag){
        return i;
    }
}
return -1;

【C++】

枚举原串 ss 中的每个字符作为「发起点」,每次从原串的「发起点」和匹配串的「首位」开始尝试匹配:

    匹配成功:返回本次匹配的原串「发起点」。
    匹配失败:枚举原串的下一个「发起点」,重新尝试匹配。

class Solution {
public:
    int strStr(string s, string p) {
        int n = s.size(), m = p.size();
        for(int i = 0; i <= n - m; i++){
            int j = i, k = 0; 
            while(k < m and s[j] == p[k]){
                j++;
                k++;
            }
            if(k == m) return i;
        }
        return -1;
    }
};

都很慢

法2:直接用strStr()返回第一个符合的位置,再减去第一个长字符串

【C】

int strStr(char * haystack, char * needle){
    if (needle==0)
    return 0;
    int ans=strstr(haystack,needle)-haystack;
    if (ans<0)
    return -1;
    else
    return ans;
}

这种很快 ,比后边kmp算法还快

法3:KMP算法,求next数组,详细解答见代码随想录

【C】

前缀表不减一版本
void getNext(int* next, char* s) {
    //初始化 next
    int j = 0;
    next[0] = j;
    for (int i = 1; i < strlen(s); i++) {	//注意 i 从 1 开始
        //若前后缀不相同
        while (j > 0 && s[i] != s[j]) {
            //则向前回退
            j = next[j - 1];
        }
        //若前后缀相同
        if (s[i] == s[j]) {
            //i 和 j 同时向后移动(i 的增加在 for 循环里)
            j++;
        }
        //将 j(前缀的长度)赋值给 next[i]
        next[i] = j;
    }
}

int strStr(char * haystack, char * needle){
    int len1 = strlen(haystack);
    int len2 = strlen(needle);
    //当 needle 为空字符串时,返回 0
    if (len2 == 0) {
        return 0;
    }

    //构建 next 数组
    int* next = malloc(sizeof(int) * len2);
    getNext(next, needle);
    //next 记录的起始位置为 0,所以这里也从 0 开始
    int j = 0;
    for (int i = 0; i < len1; i++) {	//注意匹配时 i 从 0 开始
        //若不匹配
        while (j > 0 && haystack[i] != needle[j]) {
            //j 退回到之前匹配的位置
            j = next[j - 1];
        }
        //若匹配
        if (haystack[i] == needle[j]) {
            //i 和 j 同时向后移动(i 的增加在 for 循环里)
            j++;
        }
        //当 j 等于 needle 的长度时,说明字符串 haystack 里出现了字符串 needle
        if (j == len2) {
            //返回 needle 字符串出现的第一个位置
            return (i - len2 + 1);
        }
    }
	
    //若未找到则说明不存在,返回 -1
    return -1;
}

 

 2.通过删除字母匹配到字典中的最长单词

【题目】

【分析】快排再比较

int cmp(const void* a, const void* b){
    char* ap = *(char**)a;
    char* bp = *(char**)b;
    if(strlen(ap) != strlen(bp)){
        return strlen(bp) - strlen(ap);
    }
    return strcmp(ap, bp);
}

bool canDelete(char * s, char * d){
    int sLen = strlen(s);
    int dLen = strlen(d);
    int i = 0, j = 0;
    while(i < sLen && j < dLen){
        if(s[i] == d[j]){
            j++;
            if(j == dLen){
                return true;
            }
        }
        i++;
    }
    return false;
}

char * findLongestWord(char * s, char ** dictionary, int dictionarySize){
    qsort(dictionary, dictionarySize, sizeof(char*), cmp);
    int sLen = strlen(s);
    int i, j, flag = 0;
    char* ans = malloc(sizeof(char) * (sLen + 1));
    for(i = 0; i < dictionarySize; i++){
        if(canDelete(s, dictionary[i]) && sLen >= strlen(dictionary[i])){
            strcpy(ans, dictionary[i]);
            flag = 1;
            break;
        }
    }
    if(flag == 0){
        ans = "";
    }
    return ans;
}

3.有序数组的平方

【题目】

【分析】

法1:先算平方再快排

int cmp(const void* a,const void* b){
    return (*(int*)a-*(int*)b);
}
int* sortedSquares(int* nums, int numsSize, int* returnSize){
    
    
    
    int *ans=(int*)malloc(sizeof(int)*numsSize);
    for(int i=0;i<numsSize;i++){
       ans[i]=nums[i]*nums[i];
    }
    qsort(ans,numsSize,sizeof(int),cmp);
    *returnSize=numsSize;
    return ans;
}

法2:双指针法

--直接平方再两边双指针--
int* sortedSquares(int* nums, int numsSize, int* returnSize) {
    int* ans = malloc(sizeof(int) * numsSize);
    *returnSize = numsSize;
    for (int i = 0, j = numsSize - 1, pos = numsSize - 1; i <= j;) {
        if (nums[i] * nums[i] > nums[j] * nums[j]) {
            ans[pos] = nums[i] * nums[i];
            ++i;
        } else {
            ans[pos] = nums[j] * nums[j];
            --j;
        }
        --pos;
    }
    return ans;
}
--第二种--
int* sortedSquares(int* nums, int numsSize, int* returnSize){
    int *reNums;
    int left = 0,right = numsSize - 1,i = numsSize - 1;//定义左右“指针”分别从最左和最右开始
    reNums = malloc(sizeof(int) * numsSize); //申请动态数组将原来的值计算比较赋值给新的数组即reNums
    for(int j = 0; j < numsSize; j++){  // 遍历整个数组计算各个数的平方值并重新赋值给nums
        nums[j] = pow(nums[j],2);
    }
    while(left <= right){             // 当右边大于等于左边时进入循环
        if(nums[right] >= nums[left]){//最后一次肯定是左边右边遍历到同一个位置所以等于也要判断
            reNums[i] = nums[right--];//右边赋值后像左偏移-1
        }
        else{
            reNums[i] = nums[left++];//左边赋值后向右偏移即+1
        }
        i--;
    }
    *returnSize = numsSize;//返回数组大小和传入的大小一致
    return reNums;
}

4.移动0

【题目】

【分析】

法1:循环遍历法:

void moveZeroes(int* nums, int numsSize){
    /* 方法一:先统计所有非零数、然后放到数组前端,剩余空间全补0 */
    int j = 0;
    for (int i = 0; i <= numsSize -1; i++) {
        // 第1步:找到非零数放在数组前端
        if (nums[i] != 0) {
            nums[j] = nums[i];
            j++;
        }
    }

    // 第2步:从j开始到数组末端全赋值0(j之前都是非0数)
    while (j <= numsSize-1) {
        nums[j++] = 0;
    }
}

法2:双指针法:参考:https://leetcode-cn.com/problems/move-zeroes/solution/cyu-yan-ti-jie-xun-huan-bian-li-fa-by-ma-3xgu/

/*
    方法二:双指针交换法(该题的本质是考察双指针,i 代表右指针,j代表左指针)
    0  1  0  3  12
    i
    j

    0  1  0  3  12     i指向数组第1个非零数,准备交换nums[i]和nums[j],即准备交换1和0
       i
    j

    1  0  0  3  12
       i               swap(&nums[i], &nums[j])交换,即1和0交换完成(第1轮交换)
    j

    1  0  0  3  12
             i         i移动到下一个非0数字,j向前移动1步,准备交换nums[i]和nums[j],即准备交换3和0
       j

    1  3  0  0  12
             i         swap(&nums[i], &nums[j])交换,即3和0交换完成(第2轮交换)
       j

    1  3  0  0  12
                 i     i移动到下一个非0数字(刚好是数组结尾),j向前移动1步,准备交换nums[i]和nums[j],即准备交换12和0
          j

    1  3  12  0  0
                 i     swap(&nums[i], &nums[j])交换,即12和0交换完成(第3轮交换)。由于i已经到达数组结尾,因此结束。
           j
*/

void swap(int *a, int *b) {
    int t = *a;
    *a = *b;
    *b = t;
}

void moveZeroes(int* nums, int numsSize) {
    // 方法二:双指针交换法(i为右指针, j为左指针)
    int j = 0;
    for (int i = 0; i <= numsSize -1; i++) {
        if (nums[i] != 0) {
            swap(&nums[i], &nums[j]); // 交换数组下标索引i和j对应的元素值
            j++;  // 左指针右移1步
        }
    }
}

5.反转字符串

【题目】

【分析】

双指针法:

【C】

void swap(char *a, char *b) {
    char t = *a;
    *a = *b, *b = t;
}

void reverseString(char *s, int sSize) {
    for (int left = 0, right = sSize - 1; left < right; ++left, --right) {
        swap(s + left, s + right);
    }
}

【C++】

reverse()

class Solution {
public:
    void reverseString(vector<char>& s) {
        reverse(s.begin(),s.end());
    }
};

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值