寻找子字符串——上

第十一章的习题7,刚开始觉得简单,可是实际思考时发现要考虑很多东西,然后脑子就一片混沌了,然后问题一拖一个月,然而时间并没有帮我解决掉问题,问题还是要自己去努力接触才能发展的呀。

有参考答案,上网也找到了答案,昨天验证了下,是对的。自己就是眼高手低,还犟,想凭借自己的“智慧”想出来(别人的我看了觉得费劲,而且很难理解他的思想,他到底是怎么想到的呢)。不过不能自负,遇到问题想不出来很正常,就该学习自己想不到的思路,改变自己的思维方式。应该向前人学习,可以先读懂,理解一点是一点,接触多了自己才能彻底理解,否则一直止步不前会更痛苦。

第一套方案,网上下载的CPrimerPlus的答案,贴上源码,注释上,再谈谈自己的理解。

char *string_in(char *p1, char *p2)
{
    char *p1_save = p1, *p2_save = p2;  //每个字符串分别有两个指针指着
    if(*p1 == ‘\0‘ || *p2 == ‘\0‘)     //两个字符串都是空的话就没得比了,返回空指针
        return NULL;
    while(1)                 //循环是必须的吧,一直找,直到符合跳出的标准,所以循环中要分清跳出循环的情况
    {
        if(*p1 == *p2)                //如果指向的母字符串和子字符串首元素相同
        {
            if(*++p2 == ‘\0‘) return p1_save;   //指针指向子字符串下一个元素,并且判断是否为\0:是的话就比完啦,且得到明确结论,母字符串里找到了子字符串,返回相应指针;不是的话,子字符串还没到达尾部呢(回头还得继续判断,所以用循环),运行下一步
            if(*++p1 == ‘\0‘) return NULL;     //指针指向母字符串下一个元素,并且判断是否为\0:是的话也比完啦,且得到明确结论,就是子字符串还没到尾部母字符串就到尾部了,即母字符串里是找不出子字符串的(子字符串比母字符串还长呢),返回空指针;不是的话,那我也不能得出明确的结论,所以要继续分析,走下一步吧
        }
        else                       //母字符串中被指向的元素和子字符串首元素不同
        {
            if(*++p1 == ‘\0‘) return NULL;    //指针指向母字符串下一个元素,并且判断是否为\0:是的话也没得玩儿了,结论是母字符串里找不到子字符串(母字符串到尾都没能找到子字符串的首元素,真是惨);不是的话,母字符串还没结束呢,后面还有希望哦,继续判断
            p1_save = p1;              //p1_save记住已母字符串已经往后移一位的元素位置,接下来要比较母字符串中这个元素和子字符串的首元素,如果找到了完整的子字符串,就要回头输出这个地址,所以要记住了
            p2 = p2_save;              //p2_save记住的就是子字符串的首元素的地址,这就是让指针重新指向子字符串的首元素,接下来重头与母字符串进行比较
        }
    }
}

从代码反过来去注释,去推作者的思路,然后用语言总结他解决这个问题的办法。虽然比较笨,但是自己想不到就要问别人,这个别人不在身边没法告诉你他是怎么想的,只能自己去带入代码中去换位思考,看看人家牛人是怎么想到的啦。接下来我来总结下这个牛人是怎么想的,先去吃饭,O(∩_∩)O哈哈~

用子字符串首元素和母字符串首元素比较,假设不同,指向母字符串的指针往后移一位,再和子字符串首元素比较,当找到和子字符串首元素相同的母字符串中的元素时,停下来,我想想,恩,这时应该同时将指向母字符串和子字符串的指针都往后移一位,进行比较(假设子母字符串都是不止一个元素),假设相同,再将两个指针同时后移一位,再比较,假设还相同,再后移再比较,直到指向子字符串的指针已经指向尾部\0,也就是说,此前都相同的话,说明母字符串中找到了子字符串了,返回母字符串中相应的地址。

这是只针对比较顺利的情况来描述的思路,要考虑每一个“假设”并不是那么顺利的情况。针对每一个假设,我们分别考虑周全些。假设子字符串和母字符串的首元素就相同呢?那么早就该采取子母字符串的指针同时后移再做比较的步骤了。假设母字符串中找遍了都没有子字符串首元素呢?应该返回NULL。假设子母字符串后移后(子母字符串前面相同后面又不同了)再比较发现母字符串元素和子字符串元素不同了呢?这时应该从母字符串中这个不同元素开始,和子字符串的首元素继续比较。假设子母字符串同时后移再比较都相同再后移却发现母字符串先达到尾部呢?这时说明找不到,应该返回NULL。

再说说程序设计时,while(1)的循环中,应该每次只处理或者判断一个字符,以母字符串为基础从头开始挨个进行处理。

试着设计一个伪代码(其实还是抄袭的原来的代码^_^,再结合着理解一遍的话,会更透彻一些吧):

while(1)  //循环着开始 挨个处理 “字符们”
{
  if (p1 == p2)  //子字符和母字符相同的话,子母字符都往后移一位,并且要进行判断
  {
    p2++; if(p2 == \0) return p1;  //先判断子字符串(通常会短一些),是否到达结尾了,若是的话,也就是子字符串结尾前的字符都和母字符串中的字符能对上,也就是在母字符串中找到了子字符串,返回相应指针
    p1++; if(p1 == \0) return NULL; //再判断子字符串在没有到达结尾的情况下(能运行这一步,其实也是在上一步的if不成立的条件下,即子字符没结束),母字符串是否达到结尾了。若是的话,则子字符串比母字符串长,所以返回NULL,即母字符串中找不到对应的子字符串;若不是的话,继续返回循环开始,比较移位后的字母字符。
  }
    else       //子字符和母字符不相同的话,母字符后移一位,再和子字符串比较(比较之前,要判断一下母字符串是否结束了。若是结束了,则说明母字符串到结束前没有对应上子字符串首元素,即母字符串中找不到子字符串,返回NULL;若是没结束,继续返回循环开始,比较移位后的母字符和 子字符。)
  {
    ++p1;
    if(p1 == \0) return NULL
  }
}

好了,到此为止,算是理解了这个程序吧。只能赞美作者的思路清晰,办法巧妙,自己是想不到的啊,膜拜之余,我体会到了:a)自己想不到的不要硬想,清晰的思路和逻辑一方面需要静下来思考,另外一方面是需要灵感的,所以要多多体会和理解大神们的杰作,因为很多经验或者模板是很重要的;b)从程序倒推思路虽然看似“笨”,但是也是考验一个人学习能力的,多看多琢磨多体会,思路肯定会越来越清晰的,自己要静下来努力做。最后,要达到不看原来的程序能自己实现代码的程度吧,哪怕看起来非常像也不要紧,能加强理解。

附上自己复现的代码:

char *string_in(char *p1, char *p2)
{
    char * p2_save = p2;
    while(1)                //这个可不能少啊,开始居然忘了写这个
    {
        if(*p1 == *p2)
        {
            p2++;
            if(*p2 == ‘\0‘) return (p1 - strlen(p2_save) + 1); //返回(p1指向的位置在往前退“子字符串长度”个单位)
            p1++;
            if(*p1 == ‘\0‘) return NULL;
        }
        else
        {
            p1++;
            if(*p1 == ‘\0‘) return NULL;
        }
}

总的程序如下(仅供参考,倒没什么好看的):

#include <stdio.h>
#include <string.h>

char *string_in(char *p1, char *p2);

int main(void)
{
    char str1[81];
    char str2[21];
    char *p;
    do
    {
        puts("input range string:");
        gets(str1);
        puts("input match string:");
        gets(str2);
        p = string_in(str1, str2);
        if ( p )
        {
            puts("Find!");
            puts(p);
        }
        else puts("Can‘t find!");

        puts("input any char except q to go on.");
        gets(str1);
    }
    while(*str1 != ‘q‘);
        puts("Quit.");
    return 0;
}

char *string_in(char *p1, char *p2)
{
    char * p2_save = p2;
    while(1)                //这个可不能少啊,开始居然忘了写这个
    {
        if(*p1 == *p2)
        {
            p2++;
            if(*p2 == ‘\0‘) return (p1 - strlen(p2_save) + 1); //返回(p1指向的位置在往前退“子字符串长度”个单位)
            p1++;
            if(*p1 == ‘\0‘) return NULL;
        }
        else
        {
            p1++;
            if(*p1 == ‘\0‘) return NULL;
        }   }
}

调试结果如下:

时间: 2024-11-15 03:00:57

寻找子字符串——上的相关文章

使用后缀数组寻找最长公共子字符串JavaScript版

后缀数组很久很久以前就出现了,具体的概念读者自行搜索,小菜仅略知一二,不便讨论. 本文通过寻找两个字符串的最长公共子字符串,演示了后缀数组的经典应用. 首先需要说明,小菜实现的这个后缀数组算法,并非标准,只是借鉴了其中的思想. 小菜实现的算法,有两个版本,第一个是空间换时间,第二个是时间换空间. 空间换时间版本 1 /* 2 利用后缀数组获取两个字符串最长公共子字符串 3 空间换时间版本 4 @params 5 s1 String,要分析的字符串 6 s2 String,要分析的字符串 7 no

[LeetCode] Unique Substrings in Wraparound String 封装字符串中的独特子字符串

Consider the string s to be the infinite wraparound string of "abcdefghijklmnopqrstuvwxyz", so s will look like this: "...zabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcd....". Now we have another string p. Your job is to find

输入一个字符串,输出该字符串中对称的子字符串的最大长度。

public class LongestSymmtricalLength2 { /* * Q75题目:输入一个字符串,输出该字符串中对称的子字符串的最大长度. * 比如输入字符串“google”,由于该字符串里最长的对称子字符串是“goog”,因此输出4. */ public static void main(String[] args) { String[] strs = { "a","google", "elgoog", "agol

找出一个字符串中最长重复次数的子字符串,并计算其重复次数

原题 找出一个字符串中最长重复次数的子字符串,并计算其重复次数.例如:字符串"abc fghi bc kl abcd lkm abcdefg",并返回"abcd"和2. 我的思路 为了方便表述,我们使用变量src作为原字符串,sub_str作为子字符串. 由于题目要求寻找至少重复2次的最长的子字符串,重点在于最长的子字符串,而不在于重复的最多次数.因此我们可以从长度最长的字符串入手,计算其重复次数.只要重复达到2次,即可返回该字符串. 显然长度最长的子字符串就是原串

[经典] 最X(长 | 大和 | 大积)Y(子序列 | 子字符串)

Note: 子序列,可以不连续:子字符串,必须连续. 以下题目按在我看看来的难度从易到难排列: 最大和子序列(Maximum sum subsequence) 这道题纯属娱乐...应该不会有人出这种题吧.方案是遇到正数就放入序列. vector<int> msseq(vector<int> &num) { vector<int> result; for(int i : num) if(i > 0) result.push_back(i); return r

用C#通过正则表达式截取字符串中符合条件的子字符串

仅仅作为简单的记录,不多说直接上代码(仅测试使用): private void Test() { Regex ConnoteA = new Regex("^[a-zA-Z]\\d{8}$"); Regex ConnoteAA = new Regex("^[a-zA-Z]{2}\\d{7,10}$"); Regex ConnoteAAA = new Regex("^[a-zA-Z]{3}\\d{5,9}$"); Regex ConnoteAAAA

字符串内子字符串的逆序算法

源于一道ACM的题目,要求将字符串中的子字符串逆序输出,最初的想法就是利用strtok按照空格进行分割字符,再实现以下单个字符串的逆序,再通过strcat进行字符串的拼接,感觉这个想法不错,利用库函数搞搞,但是不知道哪里出错,strcat出来的字符串输出的时候会出现多个换行,非常难受,感觉一个简单的问题,代码太多,所以睡觉的时候就在想,我应该考虑自己写一个算法来实现这个需求,刚好最近在看算法方面的书,那就尝试下吧. 基本思想,从前往后扫描字符串,遇到空格停止,字符串第一个字符start位置,扫描

NSString子字符串查找在父字符串的位置.

NSString上一章我们讲解了如何在父字符串中提取它的一部分作为一个新的子字符串而存在, 今天我们就来讲讲怎么知道子字符串在父字符串中的位置, 以及长度是多少. 这里使用的方法: NSRange: 前面我们说过, NSRange是作为一个结构体一样的存在, 它里面是由两个长整数类型构成的, 而rangOfString:就是把要判断的子字符串在这声明, 由它来返回给NSRange来判断子字符串所在父字符串的位置以及长度. 下面是简单例子: #import <Foundation/Foundati

LeetCode最小窗口子字符串以及求解子字符串模板

LeetCode原题地址 题干: 给定字符串S和T,以O(n)复杂度在S中找出包含T中所有字符的最小窗口子字符串. 示例: S = "ADOBECODEBANC"T = "ABC" 最小窗口是"BANC". 备注: 如果没有结果,返回空字符串"". 如果有多个窗口,保证只有一个最小解. Discuss中的答案(并且给出了一个求解所有子字符串问题的模板): 原题的解: 1 string minWindow(string s, s