【剑指offer专项突破版】字符串篇——“C“

news2024/11/24 0:13:49

前言

剑指offer专项突破版(力扣官网)——> 点击进入
本文所属专栏——>点击进入

一.字符串中的变位词

题目分析

在这里插入图片描述

  • 总结

要求——在字符串2中找到字符串的1的排列顺序之一
数据格式——仅包含小写字母——哈希表!
返回值——bool值

思路分析

在分析这种情况的情况我们应该在脑中装上什么条件下s2的中包含s1的某个变位词!
当然是s2的长度大于等于s1的长度的情况下,那如果 s1的长度大于s2的长度就绝对不可能有这样的字符串

 如果我们蛮力求解很容易放弃,因为s1排列组合为s1长度的阶乘,如果设s1的长度为n1的话,那么可能就会有n1! 种情况!这是一个很大的复杂度比2n次方还大!
在这里插入图片描述
这主要是排列顺序提高了算法复杂度,如果我们消除这种排列顺序的影响,那么复杂度就会提升很多,那哈希表这种数据结构就派上了大用场,开辟一个数组arr(26个元素)用于存储26个字母出现的次数,那么在用上子串s1的长度不变,定义两个指针(确定查找的长度),先在让一个指针s2中移动s1个长度,并在s1中减上s2字符串中字符出现次数,此时如果能在s2中找到一种情况满足——arr中的元素全部为0,那么就找到了。如果还没有找到,那么就让左边的指针移动再把s1中的字符次数补上,右边的指针就再减上s1的字符出现的次数。这样直到找到或者右边的指针遇到s2的边界为止

总结:这里的两个指针就好像一个在挣钱还债,一个在花钱负债,子串中的字符的出现次数就像是一个银行(存你原来有多少钱,其它位置没钱),那么当两个指针,达到这样的情况,刚好把你的原来的钱花完——arr中全部为0。

举个例子:
在这里插入图片描述

  • 总结

第一步:判断是否s2的长度大于等于s1的长度前提
第二步: 开辟数组(26个元素),用于存储子串s1的字符出现次数。
第三步:固定长度——两个指针,一个右指针(欠钱)移动s1字符串的长度,一个左指针(还钱)。
第四步:第一次移动完之后,需要判断是否刚好没钱。
第五步: 如果判断还负债,则右指针继续欠钱,左指针继续挣钱。
第六步: 当找到时,或者右指针越界时,停止返回布尔值。

代码

bool is_zero(int *arr)
{
    //当有不是0的情况就是有钱或者欠钱
    for(int i = 0; i < 26; i++)
    {
        if(arr[i] != 0)
        {
            return false;
        }
    }
    return true;
}

bool checkInclusion(char * s1, char * s2)
{
    //前提s1的长度小于等于s2的长度
    int len1 = strlen(s1);
    int len2 = strlen(s2);
    if(len1>len2)
    {
        return false;
    }
    //开辟并初始化一个数组,存储s1字符出现的次数
    int *arr =(int*)malloc(sizeof(int)*26);
    memset(arr,0,sizeof(int)*26);
    for(int i = 0; i < len1; i++)
    {
        arr[s1[i]-'a']++;
    }
    //让右指针先花钱,花到s1的长度为止
    for(int right = 0; right < len1; right++)
    {
        arr[s2[right]-'a']--;
    }
    //看这里是否刚好花完,没有负债。
    if(is_zero(arr))
    {
        return true;
    }
    //如果还没有刚好花完,就继续移动
    for(int right = len1,left = 0; right < len2;)
    {
        arr[s2[right++]-'a']--;
        arr[s2[left++] -'a']++;
        if(is_zero(arr))
        {
            return true;
        }
    }
    //释放空间
    free(arr);
    arr = NULL;
    return false;
}

二.字符串中的所有变位词

题目分析

在这里插入图片描述

  • 总结

1.数据格式——26个小写字母组成的字符串——哈希
2.要求——找到所有变位词的子串,并记录起始的索引下标。
3.返回形式——数组——malloc

思路分析

 跟上一题的思路基本一样,换汤不换药,不过我们需要自己开辟一个数组存起始位置的下标而已。

  • 总结

第一步:判断是否s的长度大于等于p的长度前提
第二步: 开辟数组(26个元素),用于存储子串p的字符出现次数。
这里需要多一步的是,开辟一个数组——大小一次开够(s长度个元素)
第三步:固定长度——两个指针,一个右指针(欠钱)移动s字符串的长度,一个左指针(还钱)。
第四步:第一次移动完之后,需要判断是否刚好没钱,如果刚好没钱需要记录起始索引
第五步: 如果判断还负债,则右指针继续欠钱,左指针继续挣钱。继续移动,有记录起始索引
第六步: 右指针越界时,就停止循环。

代码

bool is_zero(int* arr)
{
    for(int i = 0; i < 26; i++)
    {
        if(arr[i] != 0)
        {
            return false;
        }
    }
    return true;
}
int* findAnagrams(char * s, char * p, int* returnSize)
{
    int len_s = strlen(s);
    int len_p = strlen(p);
    //先开辟一个数组用于存储起始索引
    int *apos = (int*)malloc(sizeof(int)*len_s);
    *returnSize = 0;
    //前提
    if(len_p > len_s)
    {
        printf("hehe");
        return apos;
    }
    //开辟数组存储子串信息
    int *arr =(int*)malloc(sizeof(int)*26);
    memset(arr,0,sizeof(int)*26);
    for(int i = 0; i < len_p; i++)
    {
        arr[p[i]-'a']++;
    }
    //同时移动右指针,先花到len_p个长度
    for(int right = 0; right < len_p; right++)
    {
        arr[s[right]-'a']--;
    }
    //判断一下是否刚好花完

    if(is_zero(arr))
    {
        apos[(*returnSize)++] = 0;
    }
    //继续判断
    for(int left = 0,right = len_p; right < len_s;)
    {
        arr[s[right++]-'a']--;
        arr[s[left++]-'a']++;
        if(is_zero(arr))
        {
            apos[(*returnSize)++] = left;
        }
    }
    //记得释放空间
    free(arr);
    arr = NULL;
    return apos;
}

三.不含重复字符的最长子字符串

题目分析

在这里插入图片描述

  • 总结

1.数据格式——最大可能有256个字符
2.要求找到——不含有重复字符最长连续子字符串长度
3.返回值——符合要求的长度

思路分析

 既然是不含重复字符的长度,那么字符出现的次数就显得格外重要,那记录字符串出现的次数就还得用到哈希表,那么当字符在哈希表出现两次时,此时字符串中就含有相同字符。开始我们需要一个指针指向右边界,不断的在哈希表中加上字符出现的次数,这时指向左边界的指针不动,同时计算当前字符串的长度,直到某个字符在哈希表中出现两次,然后那么我们只需让左指针,调整到没有相同字符串为止。继续计算,直到右指针越界为止。

  • 总结

第一步:开辟并初始化空间存储字符出现的信息
第二步: 让右指针,在数组中加到有重复字符为止,同时计算并比较得出当前最大长度。
第三步: 让左指针,在数组中不断去字符,直到去掉重复的字符为止。

代码

int lengthOfLongestSubstring(char * s)
{
    //开辟空间,记录字符出现的次数
    int *arr = (int*)malloc(sizeof(int)*256);
    //初始化数组元素为0
    memset(arr,0,sizeof(int)*256);
    //所求最大长度
    int max_len = 0;
    //字符串长度
    int len_s = strlen(s);
    //计算思路
    bool repeat = false;
    for(int left = 0,right = 0; right < len_s;)
    {
        //先让数组中加到有重复元素为止。
        while(right < len_s && !repeat)
        {
            arr[s[right++]]++;
            //此时right已经加1了,因此我们要看的是上一个是否为2
            if(arr[s[right-1]] == 2)
            {
                repeat = true;
                break;
            }
            //此为不重复的字符串
            //下标:左闭右开
            int len = right - left;
            if(len > max_len)
            {
                max_len = len;
            }
        }
        //这里会出现重复数字,也可能是数组右指针越界
        while(left < right && repeat)
        {
            if(arr[s[left]]==2)
            {
                repeat = 0;
            }
            arr[s[left++]]--;
        }
    }
    return max_len;
}

四.含有所有字符的最短字符串

题目分析

在这里插入图片描述

  • 总结

数据格式——英文字母
要求——包含子串的最小字符串
返回——符合要求的字符串

思路分析

 思路大致与前几道相同,不过我们这里字符串的条件,最开始存的字符串都为0,即包含了子串的所有字符,其它的不用看,因此我们需要记住,这里的子串的字符的出现总次数就是子串的长度,如果访问到大于0的就是子串的字符,总次数就要减一,如果其它的则不用,直到总次数为0即可,总次数等于0,这时就要算长度比较,并且要让左边界进行调整,直到遇到有一个数,其次数哈希表中大于0,这是我们就跳过了一个子串的字符,使之又达到了不完整的状态,继续上面的动作,直到遇到右边界为止,这时又需要注意,当遇到右边界时,总次数也可能为0,还要进行判断!

  • 总结

第一步:开辟空间存储字符串,记得也要给\0留位置。开辟空间存储字母出现的次数。
第二步:进行上述思路的计算。
第三步:拷贝字符串。

char * minWindow(char * s, char * t)
{
    //求子符串长度
    int len_s = strlen(s);
    int len_t = strlen(t);
    //为存储的字符串开空间
    char* str = (char*)malloc(sizeof(char)*(len_s+1));//给\0留一个位置
    memset(str,0,sizeof(char)*(len_s+1));
    //为字符出现次数的信息开空间
    int size = 'z'-'A' + 1;
    int * arr = (int*)malloc(sizeof(int)*size);
    memset(arr,0,sizeof(int)*size);
    //算法思路
    //存放字符的信息
    for(int i = 0; i < len_t; i++)
    {
        arr[t[i]-'A']++;
    }
    //左边和右边的下标
    int begin = 1;
    int end = 0;//错开方便,判断最后没有的情况。
    int min_len = len_s + 1;//跟它比永远小
    int count = len_t;//子串字符出现的总次数
    for(int right = 0,left = 0; right < len_s||(count==0&&right==len_s); )
    {

        if(count > 0)
        {
            arr[s[right]-'A']--;
            if(arr[s[right]-'A'] >= 0)
            {
                count--;
            }
            right++;
        }
        else
        {

            int len = right - left;//左闭右开
            printf("%d",len);
            if(len < min_len)
            {
                begin = left;
                end = right;//也是左闭右开
                min_len = len;
            }
            arr[s[left]-'A']++;
            if(arr[s[left]-'A'] > 0)
            {
                count++;
            }
            left++;
        }
    }
    //拷贝字符串
    for(int i = 0,left = begin; left < end; left++,i++)
    {
        str[i] = s[left];
    }
    free(arr);
    arr = NULL;
    return str;
}

五.有效的回文

题目分析

在这里插入图片描述

  • 总结

要求:只考虑字母和数字字符,大小写忽略
返回值:返回bool值

思路分析

这道题用双指针进行判断,是否是回文了,由于这道题难度不大,具体的思路就不说了,这里我们说几个细节,字符数组的开辟要留意给\0开辟空间,并且空字符串也是回文。

  • 总结

第一步:将字符串的有效信息存储到为字符串开辟的空间中,并将大写字母转换成小写字母。
第二步:用双指针进行判断是否是回文。

代码

bool is_rsame(char* str1,int begin,int end)
{
    while(begin <= end)
    {
        if(str1[begin]!=str1[end])
        {
            return false;
        }
        begin++;
        end--;
    }
    return true;
}
bool isPalindrome(char * s)
{
	
    int len = strlen(s);
    //开辟空间存储字符串的信息
    char* str = (char*)malloc(sizeof(char)*(len+1));
    memset(str,0,sizeof(char)*(len+1));
    int size = 0;
    while((*s)!='\0')
    {
        char tmp = *s;
        if(tmp>='A'&&tmp<='Z')
        {
            tmp+=32;
            str[size++] = tmp;
        }
        else if(tmp>='a' && tmp<='z')
        {
            str[size++] = tmp;
        }
        else if(tmp>='0'&&tmp<='9')
        {
            str[size++] = tmp;
        }
        s++;
    }
    bool is_rsa = is_rsame(str,0,size-1);
    free(str);
    return is_rsa;
}

六.最多删除一个字符得到回文

题目分析

在这里插入图片描述

  • 总结

数据格式——小写字母
要求——不删是回文或者删除一个是回文
返回——布尔值

思路分析

 不删除我们很显然知道,删除一个是回文怎么删呢?如果左移删一个,不是回文,但是右移删一个,是回文,这可怎么办呢?答案——都走一遍不就知道了,只要有一边是回文的不就行了?这就是小孩子才做选择,我全都要!

  • 总结

遇到不相等时,就左边走一次,右边再走一次,两个有一个为真,就返回真,否则就返回假。

代码

bool judge(char* s,int left,int right)
{
    while(left<=right)
    {
        if(s[left]!=s[right])
        {
            return false;
        }
        left++;
        right--;
    }
    return true;
}
bool is_rsame(char* s, int left, int right)
{
    int count  = 0;
    while(left<=right)
    {
        if(s[left]!=s[right])
        {
            //有一边是回文就返回真
            if(judge(s,left,right-1)||judge(s,left+1,right))
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        left++;
        right--;
    }
    return true;
}
bool validPalindrome(char * s)
{
    int len = strlen(s);
    bool is_true = is_rsame(s,0,len-1);
    if(is_true)
    {
        return true;
    }
    return false;
}

七.回文子字符串的个数

题目分析

在这里插入图片描述

  • 总结

1.数据格式——小写英文字母
2.要求——求字符串有多少个回文字符串
3.返回值——回文字符串的个数

思路分析

Manacher 算法,俗称"马拉车算法",适合这类题。
这里就不多赘述了,得扯好大一会儿,详见——

1.最长回文子串问题
2.用心制作,来理解Manacher算法吧(力扣,最长回文子串)

代码——马拉车算法

int countSubstrings(char * s)
{
    //第一步:处理字符串
    //为了防止越界的问题发生,我们在开头和结尾放上两个不同的特殊字符,还要为\0留位置
    //两个特殊的字符我们用$和!
    //中间的字符串的分割符用#
    //字符串的长度为n因此需要有n+1个间隔
    //总计:需要开辟n + n+1 + 2 +1总共2*n+4个空间
    int len = strlen(s);
    char *str = (char*)malloc(sizeof(char)*(2*len + 4));
    memset(str,0,sizeof(char)*(2*len+4));
    //开头和结尾放上特殊字符
    str[0] = '$';
    str[2*len+2] = '!';
    for(int i = 0; i < len; i++)
    {
        str[2*i + 1] = '#';
        str[2*i + 2] = s[i];
    }
    str[2*len+1] = '#';
    //处理好字符串后,我们要开辟一个空间存储,中心下标最长回文子串中子串的个数
    //除去\0总共有2*len + 3个,因此要开这么大个数组
    int *nums = (int*)malloc(sizeof(int)*(2*len+3));
    memset(nums,0,sizeof(int)*(2*len+3));
    //马拉车算法思路
    //nums[i]是用于记录回文子串的个数
    //i可以说就是中心下标
    int r = 1;//回文半径
    for(int i = 1; i < 2*len+2;)
    {
        //开始进行两边拓展
        //根据中心下标可以得出对称位置的下标
        int left = i - r + 1;
        int right = i + r - 1;
        int count = r-1;
        while(str[left]==str[right])
        {
            left--;
            right++;
            count++;
        }
        left++;
        right--;
        //记录当前中心下标的半径
        nums[i] = count;
        //到这里right如果等于i或者i+1,就一个
        if(i==right||i==right-1)
        {
            r = 1; 
            i++;
            continue;
        }
        if(i < right)
        {
            //当前初始位置为中心元素的下一个位置
            int k = i + 1;
            int flag = 0;
            while(k < right)
            {
                //求左边的对称点
                int n = 2*i - k;
                int n_left = n-nums[n]+1;
                if(n_left<=left)
                {
                    //更新半径
                    r = 1;
                    i = k;
                    break;
                }
                else
                {
                    nums[k] = nums[n];
                }
                k++;
            }
        }
    }
    //计算求和
    int sum = 0;
    for(int i = 0; i < 2*len+3; i++)
    {
        sum+=(nums[i]/2);
    }
    return sum;
}

希望有所帮助!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/624625.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

国产数据库有哪些?关键行业数据库为何一定要国产化?

国产数据库主要有以下几种&#xff1a; 1. 中国数据库&#xff08;ChinaDB&#xff09;&#xff1a;由中国科学院计算技术研究所开发的关系型数据库系统。 2. 华为GaussDB&#xff08;高斯数据库&#xff09;&#xff1a;国内首个软硬协同、全栈自主的国产数据库GaussDB&…

操作系统-内存管理-内存管理

目录 一、内存概念 1.1程序的链接 静态链接 装入时动态链接 运行时动态链接 1.2 程序的装入 绝对装入 可重定位装入(静态重定位) 动态运行时装入(动态重定位) 1.3内存空间的扩充 1.3.1覆盖 1.3.2交换技术 1.4连续管理分配管理方式 1.4.1单一连续分配 1.4.2固定分区分…

设计师必备!Axure RP10汉化版下载,一站式工具满足所有需求!

Axure RP10 汉化版是一款全新的原型、设计、交付工具&#xff0c;它兼容 Axure、Figma、Sketch 和 Adobe XD 等格式&#xff0c;提供一站式协同设计&#xff0c;帮助团队高效工作。在本文中&#xff0c;我们将为你介绍 Axure RP10 汉化版即时设计的下载和安装步骤&#xff0c;让…

c++学习——多态

多态 **多态的语法****多态的底层原理图****多态案1——计算机类****纯虚函数和抽象类****多态案例2——饮品****虚析构和纯虚析构****多态案例3—— 电脑组装** 多态是C面向对象三大特性之一 多态分为两类 静态多态:函数重载和运算符重载属于静态多态&#xff0c;复用函数名 动…

ssm本地上传文件

SSM实现图片本地上传并保存到本地磁盘中 功能描述 实现房屋租赁网站中添加房屋信息的功能。其中add.jsp页面是一个表单提交信息——添加房屋 首先输入房屋的相关信息&#xff0c;并上传房屋的图片。上传成功后会将图片的名字添加到数据库中成功后跳转到success.jsp&#xff…

华为OD机试 Java 实现【简单密码】【牛客练习题 HJ21】,附详细解题思路

一、题目描述 现在有一种密码变换算法。 九键手机键盘上的数字与字母的对应&#xff1a; 1--1&#xff0c; abc--2, def--3, ghi--4, jkl--5, mno--6, pqrs--7, tuv--8 wxyz--9, 0--0&#xff0c;把密码中出现的小写字母都变成九键键盘对应的数字&#xff0c;如&#xff1a;a …

【PaperReading】科学可重复的基因组富集: CERNO 与其他八种算法的比较

Gene set enrichment for reproducible science: comparison of CERNO and eight other algorithms 可重复性科学的基因组富集: CERNO 与其他八种算法的比较1. 引言2. 材料和方法2.1 CERNO算法2.2 进一步用于评估的算法2.3 数据集2.4 算法评估指标3. 结果3.1 CERNO算法的不同排…

【前端 - HTML】第 2 课 - HTML 标签

欢迎来到博主 Apeiron 的博客&#xff0c;祝您旅程愉快 &#xff01; 时止则止&#xff0c;时行则行。动静不失其时&#xff0c;其道光明。 目录 1、缘起 2、标题标签 3、段落标签 4、文本格式化标签 5、图像标签 5.1、基本作用 5.2、属性 6、超链接标签 7、音频标…

STM32 FSMC机制

引 言 STM32是ST(意法半导体)公司推出的基于ARM内核Cortex&#xff0d;M3的32位微控制器系列。Cortex&#xff0d;M3内核是为低功耗和价格敏感的应用而专门设计的&#xff0c;具有突出的能效比和处理速度。通过采用Thumb&#xff0d;2高密度指令集&#xff0c;Cortex&#xff0…

五种I/O模型

一、I/O基本概念 I/O即数据的读取&#xff08;接收&#xff09;或写入&#xff08;发送&#xff09;操作 通常用户进程中的一个完整I/O分为两个阶段 (1)用户进程空间<-->内核空间 (2)内核空间<-->设备空间&#xff08;磁盘、网卡等&#xff09; I/O分为内存I/O、…

个人博客网站实现微信扫码登录(新)

前言 在不久之前&#xff08;两年前&#xff09;我写了一篇同名的博客《个人博客网站实现微信扫码登录&#xff08;附源码&#xff09;》&#xff0c;当时只是做一个记录而已。但是没想到会收到很多“猿友”的私聊&#xff0c;“代码跑不起来”、“实现原理”、“测试网址访问不…

[网鼎杯 2018]Fakebook1

拿到题目后是一个博客的界面&#xff0c;这里可以登录和注册 点入登录界面&#xff0c;猜测可能是sql注入 试了很多次&#xff0c;都不是&#xff0c;也没有回显报错&#xff0c;所以把目光放到了注册上面 注册的其他行数据&#xff0c;差不多都可以乱填&#xff0c;只有一个bl…

王道考研计算机网络第二章知识点汇总

2.1.1物理层基本概念 电气特性和功能特性易混淆&#xff0c;注意区分。电气特性一般指的是某个范围&#xff0c;功能特性一般指的是电平所代表的含义。 2.1.2数据通信基础知识 同步传输是指发送方和接收方节奏是统一的&#xff0c;数据之间是没有间隔的是一个一个的区块。在键…

轻松掌握Python自动化工具,解锁PyAutoGUI的强大功能

前言 PyAutoGUI是一个用于图像识别和鼠标/键盘控制的Python库。它提供了一组函数和方法&#xff0c;用于自动化屏幕上的鼠标移动、点击、拖拽和键盘输入&#xff0c;以及执行图像识别和处理。本文旨在帮助读者入门 PyAutoGUI&#xff0c;理解其基础概念和掌握最佳实践&#xff…

手把手教你实现—基于OpenCV的车流量统计和车速检测代码

本章将实现了一个简单的车辆速度估计和车流量统计的GUI应用&#xff0c;它使用了Haar级联检测器和相关跟踪器来检测和跟踪视频中的车辆&#xff0c;并通过图像处理和数学计算来估计车辆的速度。 1.首先&#xff0c;该代码需要cv2&#xff1a;用于图像处理和计算机视觉任务&…

软件测试(功能、接口、性能、自动化)详解

一、软件测试功能测试测试用例编写是软件测试的基本技能&#xff1b;也有很多人认为测试用例是软件测试的核心&#xff1b;软件测试中最重要的是设计和生成有效的测试用例&#xff1b;测试用例是测试工作的指导&#xff0c;是软件测试的必须遵守的准则。黑盒测试常见测试用例编…

比较18个3*6尺寸差值结构的迭代次数

已发现二值化差值结构有3种相互作用&#xff0c;纵向&#xff0c;横向和斜向。纵向相互作用只与行间距有关而与数值的数量无关&#xff0c;与迭代次数成反比&#xff1b;横向相互作用只与列的数值数量有关与列间距无关&#xff0c;与迭代次数成正比&#xff1b;斜向相互作用将导…

完全免费PNG素材库,免费可商用~

推荐的这几个PNG素材网一定要收藏~免费可商用~ 菜鸟图库 https://www.sucai999.com/searchlist/66008----all-0-1.html?vNTYxMjky 菜鸟图库是一个为新手设计师提供免费素材的网站&#xff0c;站内有非常多设计相关素材&#xff0c;比如平面模板、UI素材、电商素材、免抠素材…

C++中string类的常用函数

文章目录 默认成员函数常见构造函数(constructor) string类的容量操作size()empty()capacity()reserve()clear()resize() string类对象的访问及遍历操作重载 [ ]begin()end()begin() end() 遍历字符串rbegin()rend()rbegin() rend()反向遍历字符串C11范围for string类对象修改…

客户至上 服务至极 ——优维服务流程标准化体系

◎ 如何用服务打动客户&#xff1f; 在思考这个问题之前&#xff0c;首先我们要了解做好服务最难的是什么&#xff1f; 众所周知&#xff0c;由机器作业出来的东西是一致且规范的&#xff0c;而服务不一样。服务&#xff0c;是需要人来参与的&#xff0c;当由不同的人来完成某…