热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

KMP算法的应用(二十七)

        我们在上节博客中讲到了KMP算法的具体实现,那么我们本节就来看看KMP算法的应用。问题:如何在目标字符串中查找是否存在指定的子串?        我们来看看字符串类

        我们在上节博客中讲到了 KMP 算法的具体实现,那么我们本节就来看看 KMP 算法的应用。问题:如何在目标字符串中查找是否存在指定的子串

        我们来看看字符串类中的新功能,如下图所示

图片.png

       1、子串查找(KMP  算法的直接应用)

            int indexOf(const char* s) const;

            int indexOf(const String& s) const;

        我们来看看具体源码的实现,如下

int String::indexOf(const char* s) const
{
    return kmp(m_str, s ? s : "");
}
int String::indexOf(const String& s) const
{
    return kmp(m_str, s.m_str);
}

        直接利用我们上节博客实现的 kmp 函数就能实现 indexOf 函数,我们来看看效果

#include 
#include 
#include "DTString.h"
using namespace std;
using namespace DTLib;
int main()
{
    String s = "ababax";
    cout << s.indexOf("bax") << endl;
    return 0;
}

        编译运行结果如下

图片.png

        我们看到返回的位置为下标为 3 处,也就是 s[3] 处。

        2、在字符串中将指定的子串删除

            String& remove(int i, int len);

            String& remove(const char* s);

            String& remove(const String& s);

        根据 KMP 在目标字符串中查找子串的位置,再通过子串位置和子串长度进行删除。具体源码实现如下

String& String::remove(int i, int len)
{
    if( (0 <= i) && (i < m_length) )
    {
        int n = i;
        int m = i + len;
        while( (n < m) && (m < m_length) )
        {
            m_str[n++] = m_str[m++];
        }
        m_str[n] = '\0';
        m_length = n;
    }
    return *this;
}
String& String::remove(const char* s)
{
    return remove(indexOf(s), s ? strlen(s) : 0);
}
String& String::remove(const String& s)
{
    return remove(indexOf(s), s.length());
}

        我们来写个测试代码进行测试,如下

#include 
#include 
#include "DTString.h"
using namespace std;
using namespace DTLib;
int main()
{
    String s = "ababax";
    cout << s.remove("bab").str() << endl;
    return 0;
}

        我们来看看结果是不是 aax,如下

图片.png

        3、字符串的减法操作定义(operator -)

        使用 remove 实现字符串间的减法操作,必须得保证字符串自身不被修改,返回产生的新串。最后效果如下

图片.png

            String operator - (const String& s) const;

            String operator - (const char* s) const;

            String& operator -= (const String& s);

            String& operator -= (const char* s);

        利用原有的 + 操作符进行改装即可,源码实现如下

String String::operator - (const String& s) const
{
    return String(*this).remove(s);
}
String String::operator - (const char* s) const
{
    return String(*this).remove(s);
}
String& String::operator -= (const String& s)
{
    return remove(s);
}
String& String::operator -= (const char* s)
{
    return remove(s);
}

        我们来测试下,测试代码如下

#include 
#include 
#include "DTString.h"
using namespace std;
using namespace DTLib;
int main()
{
    String s = "ababax";
    String s1 = s - "bax";
    cout << s.str() << endl;
    cout << s1.str() << endl;
    s -= s;
    cout << "[" << s.str() << "]" << endl;
    return 0;
}

        我们来看看运行结果,s1 = "aba", 最后的 s 应该为空,我们来看看结果

图片.png

        我们看到结果正如我们所分析的那样。

        4、字符串中的子串替换

            String& replace(const char* t, const char* s);

            String& replace(const String& t, const char* s);

            String& replace(const char* t, const String& s);

            String& replace(const String& t, const String& s);

        我们来看看具体源码的实现

String& String::replace(const char* t, const char* s)
{
    int index = indexOf(t);
    if( index > 0 )
    {
        remove(t);
        insert(index, s);
    }
    return *this;
}
String& String::replace(const String& t, const char* s)
{
    return replace(t.m_str, s);
}
String& String::replace(const char* t, const String& s)
{
    return replace(t, s.m_str);
}
String& String::replace(const String& t, const String& s)
{
    return replace(t.m_str, s.m_str);
}

        我们来写个测试代码进行测试,代码如下

#include 
#include 
#include "DTString.h"
using namespace std;
using namespace DTLib;
int main()
{
    String s = "ababax";
    s.replace("baba", "xyz");
    cout << s.str() << endl;
    return 0;
}

        我们来看看最后的结果是不是 axyzx,运行结果如下

图片.png

        5、从字符串中创建子串

            String sub(int i, int len) const;

        以 i 为起点提取长度为 len 的子串,子串提取保证不会改变字符串本身的状态。如下

图片.png

        具体源码实现如下

String String::sub(int i, int len) const
{
    String ret;
    if( (0 <= i) && (i < m_length) )
    {
        if( len < 0 ) len = 0;
        if( len > m_length ) len = m_length - i;
        char* str = static_cast(malloc(len + 1));
        strncpy(str, m_str + i, len);
        str[len] = '\0';
        ret = str;
    }
    else
    {
        THROW_EXCEPTION(IndexOutOfBoundsException, "Parameter i is invalid ...");
    }
    return ret;
}

        我们来写个测试代码进行测试,测试代码如下

#include 
#include 
#include "DTString.h"
using namespace std;
using namespace DTLib;
int main()
{
    String s = "ababax";
    String s1 = s.sub(3, 10);
    String s2 = s.sub(2, 3);
    cout << s.str() << endl;
    cout << s1.str() << endl;
    cout << s2.str() << endl;
    return 0;
}

        最后的结果应该是 s = "ababax" ; s1 = "bax" ; s3 = "aba" ; 运行结果如下

图片.png

        现在我们的字符串类已经实现的有点规模了。通过今天对字符串类的学习,总结如下:1、字符串类是工程开发中必不可少的组件;2、字符串中应该包含常用的字符串操作函数,a> 增:insert, operator + , ...; b> 删:remove,operator -,...; c> 查:indexOf,...; d> 改:replace,... 。


推荐阅读
  • 在洛谷 P1344 的坏牛奶追踪问题中,第一问要求计算最小割,而第二问则需要找到割边数量最少的最小割。通过为每条边附加一个单位权值,可以在求解最小割时优先选择边数较少的方案,从而同时解决两个问题。这种策略不仅简化了问题的求解过程,还确保了结果的最优性。 ... [详细]
  • 2012年9月12日优酷土豆校园招聘笔试题目解析与备考指南
    2012年9月12日,优酷土豆校园招聘笔试题目解析与备考指南。在选择题部分,有一道题目涉及中国人的血型分布情况,具体为A型30%、B型20%、O型40%、AB型10%。若需确保在随机选取的样本中,至少有一人为B型血的概率不低于90%,则需要选取的最少人数是多少?该问题不仅考察了概率统计的基本知识,还要求考生具备一定的逻辑推理能力。 ... [详细]
  • 单链表的高效遍历及性能优化策略
    本文探讨了单链表的高效遍历方法及其性能优化策略。在单链表的数据结构中,插入操作的时间复杂度为O(n),而遍历操作的时间复杂度为O(n^2)。通过在 `LinkList.h` 和 `main.cpp` 文件中对单链表进行封装,我们实现了创建和销毁功能的优化,提高了单链表的使用效率。此外,文章还介绍了几种常见的优化技术,如缓存节点指针和批量处理,以进一步提升遍历性能。 ... [详细]
  • 寒假作业解析:第三周 2月12日 第7题
    尽快完成之前的练习任务!每日一练2.1 Problem A Laurenty and Shop 的题目要求是选择两条不同的路线以最小化总的等待时间。简要分析:通过对比不同路线的等待时间,可以找到最优解。此问题可以通过动态规划或贪心算法来解决,具体取决于路线的复杂性和约束条件。 ... [详细]
  • 在探讨P1923问题时,我们发现手写的快速排序在最后两个测试用例中出现了超时现象,这在意料之中,因为该题目实际上要求的是时间复杂度为O(n)的算法。进一步研究题解后,发现有选手使用STL中的`nth_element`函数成功通过了所有测试点。本文将详细分析这一现象,并提出相应的优化策略。 ... [详细]
  • Objective-C 中的委托模式详解与应用 ... [详细]
  • 图论入门基础教程
    图论是计算机科学和数学中的重要分支,本教程旨在为初学者提供全面的基础知识。通过实例解析,如“昂贵的聘礼”问题,讲述了一个年轻探险家在印第安部落与酋长女儿的爱情故事,展示了图论在解决实际问题中的应用。教程内容涵盖了图的基本概念、表示方法以及常见算法,适合各类读者学习。 ... [详细]
  • 在本文中,我们将为 HelloWorld 项目添加视图组件,以确保控制器返回的视图路径能够正确映射到指定页面。这一步骤将为后续的测试和开发奠定基础。首先,我们将介绍如何配置视图解析器,以便 SpringMVC 能够识别并渲染相应的视图文件。 ... [详细]
  • 本文介绍了如何在iOS平台上使用GLSL着色器将YV12格式的视频帧数据转换为RGB格式,并展示了转换后的图像效果。通过详细的技术实现步骤和代码示例,读者可以轻松掌握这一过程,适用于需要进行视频处理的应用开发。 ... [详细]
  • 深入解析C语言中的动态规划算法:以背包问题为例
    本文深入探讨了C语言中动态规划算法的应用,以经典的背包问题为例进行详细解析。通过实例分析,展示了如何利用动态规划解决复杂优化问题,并提供了高效的代码实现方法。文章不仅涵盖了算法的基本原理,还讨论了其在实际编程中的应用技巧和优化策略,为读者提供了全面的理解和实践指导。 ... [详细]
  • 具备括号和分数功能的高级四则运算计算器
    本研究基于C语言开发了一款支持括号和分数运算的高级四则运算计算器。该计算器通过模拟手算过程,对每个运算符进行优先级标记,并按优先级从高到低依次执行计算。其中,加减运算的优先级最低,为0。此外,该计算器还支持复杂的分数运算,能够处理包含括号的表达式,提高了计算的准确性和灵活性。 ... [详细]
  • 本文探讨了利用JavaScript实现集合的对称差集算法的方法。该算法旨在处理多个数组作为输入参数,同时保留每个数组中元素的原始顺序。算法不会移除单个数组内的重复元素,但会删除在不同数组之间出现的重复项。通过这种方式,能够有效地计算出多个数组的对称差集。 ... [详细]
  • 本文探讨了 Java 中 Pair 类的历史与现状。虽然 Java 标准库中没有内置的 Pair 类,但社区和第三方库提供了多种实现方式,如 Apache Commons 的 Pair 类和 JavaFX 的 javafx.util.Pair 类。这些实现为需要处理成对数据的开发者提供了便利。此外,文章还讨论了为何标准库未包含 Pair 类的原因,以及在现代 Java 开发中使用 Pair 类的最佳实践。 ... [详细]
  • 深入理解 Java 控制结构的全面指南 ... [详细]
  • 无论是计算机专业学生还是非计算机专业的学习者,在掌握C语言的过程中可能会遇到诸多挑战,不清楚从何入手。为此,本文系统地梳理了2019年福建省C语言的核心知识点,并结合最新的技术进展进行了详细总结,旨在为初学者提供全面的学习指导。文章不仅涵盖了基础语法和数据结构,还深入探讨了指针、内存管理和算法优化等高级主题,帮助读者快速提升编程能力。 ... [详细]
author-avatar
mobiledu2502882517
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有