Leetcode——Longest Common Prefix(包括几中常见的运行问题,runtime error以及处理方式,使用C语言实现,包含一些关于char **的知识点,良心)

题目描述

Write a function to find the longest common prefix string amongst an array of strings.

写一个函数,找到一个字符串数组中的最长的共有的前缀

If there is no common prefix, return an empty string “”.

如果没有公共前缀,返回一个空的字符串“”

Example 1:

 Input: ["flower","flow","flight"]
 Output: "fl"

Example 2:

 Input: ["dog","racecar","car"]
 Output: ""
 Explanation: There is no common prefix among the input strings.

Note:

All given inputs are in lowercase letters a-z.

所有的输入字符都是由a-z的26个小写字母构成

思路分析

方法一:逐个字符进行比对
  • 从第一个字符串开始,遍历第一个字符串的每一个字符,然后在和后面所有的字符串对应的位置的字母进行比较。同则继续迭代;不同就退出。
    • 小问题:如何容纳最终的结果:
      • 创建和第一个字符一样长的字符数组。
方法二:调用字符串直接进行字符串整体比较运算。
  • 先是整体进行比较。结果为0,说明二者相同;结果为非零,说明二者不相同。
  • 不同的情况下,向下折半,相同就向上折半。
  • 知识点补充:
    • strncmp(字符串1,字符串2,比较的字符串的数目n),比较两个字符串从零到n个字符的大小。结果为正,前面的字符大;结果为负,后面的字符串大;结果为零,两者相等。
      在这里插入图片描述

代码实现

brute force(暴力法)——占内存,耗时间,不建议采用
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char *longestCommonPrefix(char **strs, int sresSize);
int main()
{
    
    
    //今日份leetcode
    char *str1[] = {
    
    ""};
    printf("%s \n",longestCommonPrefix(str1,1));
    return 0;
}

/*
    功能描述:获取所有的字符串的最长的公共子串
*/
char *longestCommonPrefix(char ** strs, int strsSize){
    
    

    //创建一个和第一个字符串等长的内存空间
    char *result = NULL;
    result = (char *)malloc(sizeof(strs[0])+1);
    if(!result)
    {
    
    
        printf("申请内存失败");
        return NULL;
    }
    memset(result,0,sizeof(strs[1]) + 1);

    //从第一个字符串开始遍历
    for(int i = 0;i < strlen(strs[0]);i ++)
    {
    
    
        for(int j = 1;j < strsSize;j ++)
        {
    
    
            if(strs[0][i] != strs[j][i] && i < strlen(strs[j]))
            {
    
    
                goto end;
            }
        }
        result[i] = strs[0][i];
    }

end:
    return result;

}
折半查找法
int IterateStrcmp(char **strs,int strsSize,int middle)
{
    
    
    int temp = 0;
    for(int i = 1;i < strsSize;i ++)
    {
    
    
        temp = strncmp(strs[0],strs[i],middle);
        if(temp)
        {
    
    
            break;
        }
    }
    return temp;
}

/*
    功能描述:采用折半比较法,来快速判定
*/
char *longestCommonPrefix(char **strs, int strsSize)
{
    
    
    //排除空数组的情况
    if(strsSize < 1)
    {
    
    
        return "";
    }

    //单个数组直接返回
    if(strsSize == 1)
    {
    
    
        return strs[0];
    }

    //
    int end = strlen(strs[0]), temp = 0;
    for(int i = 1;i < strsSize;i ++)
    {
    
    
        temp = strlen(strs[i]);
        if(end > temp)
        {
    
    
            end = temp;
        }
    }
    int start = 0, middle = end;
    while(1)
    {
    
    
        if(!IterateStrcmp(strs,strsSize,middle))
        {
    
    
            if((IterateStrcmp(strs,strsSize,middle + 1) || middle == start)||(middle == end))
            {
    
    
                break;
            }
            else
            {
    
    
                start = middle;

            }
        }
        else
        {
    
    
            end = middle;
        }
        middle = (start + end) / 2;
    }

    //创建一个和第一个字符串等长的内存空间
    char *result = NULL;
    result = (char *)malloc(middle * sizeof(char)+1);
    if(!result)
    {
    
    
        printf("申请内存失败");
        return NULL;
    }
    memset(result,0,middle * sizeof(char)+1);

    strncpy(result,strs[0],middle);
    return result;


}

在这里插入图片描述

关于char **和字符串的一些浅显的认知

  • char **strs:C语言学的比较水,这个含义没看懂。其实就是char * [ ]的意思。数组和指针可以互换使用,一维数组为例,int a[] 可以用a来代表指针,这样获取数组元素*a
 char *str1[] = {
    
    "asdf","asder","asdcv","asdwe","asdfgh"};
  • 关于字符串数组的总结:
    • char *strs[]:
      • 数组名strs,表示指向第一个字符串的指针的地址,要获取字符串的指针还需要进行解引用计算
      • *strs 等价于strs[0]
      • *(strs+1) 等价于strs[0]
    • char **strs:
      • 可以用这种方式声明字符串数组,用来做字符串数组的形参表示,但是传入的实际上是以char *[]声明的字符串数组

在这里插入图片描述

  • char **p的使用注意:
    • 当有如下的声明时,p仅仅指向第一个字符串的地址。而且在存储字符串常量的静态存储区仅仅有第一个字符串“asdfll”,后面的字符串都没有写入其中。
    • 对P进行-/+1,并不能指向下一个字符串,本质上是在原先的地址上加上一个指针变量的长度,64位操作系统的指针就是8个字节,就是加8
    • 指针变量的字节数取决于操作系统的所能操作的位数。32位机,就是有4个字节,一个字节八位
char **strs = {
    
    "asdfll","asder","asdcv","asdwe","asdfgh"};
  • 如何将char型的字符和char *型的字符串连接起来。
    • char *p[] = {},字符串该用数组的调用方式,p[x]单个调用其中一个字符返回。
    • 字符串之间的连接,用strcat函数,strcat(前一个字符串,后一个字符串);

关于折中查找的一些肤浅认知

  • 一定要有两个标志,start和end,才可以进一步确定中间的结点,再往后迭代的迭代的过程中不会出现重复的问题
  • 这题跟普通的二分查找不一样。第一次比对应该是以最短的字符串长度为比较的标准,即middle = end,所以迭代条件应该有所修改放到循环提的后面

在这里插入图片描述

错误锦集

  • runtime error问题1:

在这里插入图片描述

 * 原因:当输入的数组为“[]”,不带任何的元素,程序将之有效的排除,所以会出现访问越界,增加如下段代码。
char *longestCommonPrefix(char ** strs, int strsSize){
    
    
    //排除输入空数组的情况
        if(strsSize < 1)
    {
    
    
        return "";
    }
    
    //创建一个和第一个字符串等长的内存空间
    char *result = NULL;
    result = (char *)malloc(sizeof(strs[0])+1);
    if(!result)
    {
    
    
...
  • runtime error问题2:

在这里插入图片描述
在这里插入图片描述

  • 问题原因:没有足够的存储空间去存储char型数组。
  • 误区:遍历整个数组,后去其中最短的字符串的长度,并不能解决。程序运行的时间过长。同时遍历第一个字符串的每一个字符仍旧不能节省时间,因此直接选出最短的,然后从最短的开始比较。
 //找到数组中长度最短的元素
    int min = 0;
    for(int i = 0;i < strsSize;i ++)
    {
    
    
        if(strlen(strs[min]) > strlen(strs[i]))
        {
    
    
            min = i;
        }
    }
结果:
  • 当输入足够长,项数足够多,每条都等长,相同的数组的时候,自动溢出宕机。说明方法不对,更换方法。
借鉴和学习
Horizontal scanning(水平扫描)

在这里插入图片描述

  • 单体比较优于整体比较,确定了方向只不断往回缩减,不会出现增加的情况。
  • 首先比较前两个字符串,确定了前两个字符串的最长字串前缀,再用该最长前缀字串和后面的字符串进行比较,确知道最后的结果。
Vertical Scanning

遍历所有的单词的同一个序号的字母,相同就继续,不同就返回,跟我的暴力破解法相同

Divide and conquer

在这里插入图片描述

  • 在horitoncal scan水平扫描的基础上,采用递归的方式。将原来的字符串数组对半分,再分,直到分到仅剩唯一的字符串,然后再进行水平扫描,获取最短的公共最长的前缀。
  • 归并排序图示

在这里插入图片描述

拓展概念

  • Trie:称为单词查找树,字典树
  • 图示
  • 作用:应用于统计,排序和保存大量的字符串,常被用于搜索引擎系统去做文本词频统计
  • 优点:利用字符串的公共前缀去减少查询时间,最大限度的减少无谓的字符串的比较

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/Blackoutdragon/article/details/108248193