数据结构(09)_字符串类的实现

1.字符串类的创建(上)

1.1.历史遗留问题:

C语言不支持真正意义上的字符串,使用字符指针和字符数组实现字符串存储,使用C库函数实现字符串操作。
C++为了兼容C语言,也不支持原生的字符串类型,但可以通过自定义类类型完成字符串类型的定义。

1.2.DTstring的设计

继承自顶层父类Object,内部封装C语言中的字符串操作。
1.无缝实现String对象和char*字符串的互操作;
2.操作符重载函数需要考虑是否支持const版本
3.通过C语言中的字符串函数实现String的成员函数
数据结构(09)_字符串类的实现
DTstring声明:

class String : Object
{
protected:
    char* m_str;
    int m_length;
    void init(const char* s);
    bool equal(const char* l, const char* r, int len) const;

public:
    String();
    String(char c);
    String(const char* s);
    String(const String& s);

    int length() const;
    const char* str() const;

    bool startWith(const char* s) const;
    bool startWith(const String& s) const;
    bool endOf(const char* s) const;
    bool endOf(const String& s) const;
    int indexOf(const char* s) const;
    int indexOf(const String& s) const;

    String& insert(int i, const char* s);
    String& insert(int i, const String& s);
    String& remove(int i, int len);
    String& remove(const char* s);
    String& remove(const String& s);

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

    String& trim();

    String sub(int i, int len) const;

    char& operator [] (int i);
    char& operator [] (int i) const;

    bool operator ==(const String& s) const;
    bool operator ==(const char* s) const;
    bool operator !=(const String& s) const;
    bool operator !=(const char* s) const;
    bool operator >(const String& s) const;
    bool operator >(const char* s) const;
    bool operator <(const String& s) const;
    bool operator <(const char* s) const;
    bool operator >=(const String& s) const;
    bool operator >=(const char* s) const;
    bool operator <=(const String& s) const;
    bool operator <=(const char* s) const;

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

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

    int* make_pmt(const char* p);
    int kmp(const char*s,const char* p);

    ~String();
};

2.字符串类的创建(下)

字符串类中的常用成员函数:
数据结构(09)_字符串类的实现
1.重载数组访问操作符[]
char& operator [] (int i);
char& operator [] (int i) const;
注意事项:
当i值取值不合法时,抛出异常。合法范围(0<=i) && (i<m_length)
2.判定是否以指定字符串开始或结束
bool startWith(const char s) const;
bool startWith(const String& s) const;
bool endOf(const char
s) const;
bool endOf(const String& s) const;
数据结构(09)_字符串类的实现
3.在指定位置插入字符串
String& insert(int i, const char* s);
String& insert(int i, const String& s);
数据结构(09)_字符串类的实现
4.去掉字符串两端的空白字符串
String& trim();
数据结构(09)_字符串类的实现
思考:如何在目标字符串中查找是否存在指定的子串?

3.KMP子串查找算法

问题:如何在目标字符串中查找是否存在指定的子串?

3.1.朴素解法

先比对子串和目标字符串的第一个字符,如果匹配则继续匹配第二个字符...
如果匹配失败,则从目标字符串的第二个字符与子串进行匹配, ...
通过两个嵌套的for循环完成,最终的时间复杂度为O(n^2)。

3.2.KMP子串查找法

朴素解法的一个优化方向:
数据结构(09)_字符串类的实现
因为, Pa != Pb,且Pb = Sb;所以Pa != Sb,结论,子串p右移1位比较没有意义。
数据结构(09)_字符串类的实现
伟大的发现:
1.匹配失败时的右移位数与子串本身相关,与目标串无关;
2.移动位数 = 已知匹配的字符数 - 对应的部分匹配值
3.任意一个子串都存在一个唯一的部分匹配表。

3.2.1.部分匹配表

数据结构(09)_字符串类的实现
那么部分匹配表是如何得到的呢?

  • 前缀:除了最后一个字符外,一个字符串的全部头部组合;
  • 后缀:除了最后一个字符外,一个字符串的全部尾部组合;
  • 部分匹配值:前缀和后缀最长共有元素的长度。
    下面以ABCDABD为例:
    数据结构(09)_字符串类的实现
    上面我们使用求前缀和后缀交集最大长度的方法得到了部分匹配值,但如何通过编程来产生部分匹配表呢?
    实现关键:
    1.PMT[1] = 0(下标为1的字符开始递推)
    2.从第二个字符开始递推(从下标为1的字符开始递推)
    3.假设PMT[n] = PMT[n-1]+1(最长共有元素的长度)
    4.当假设不成立,PMT[n]在PMT[n-1]的基础上减小,直至为0
    数据结构(09)_字符串类的实现
    总结:
    1.部分匹配表是提高子串查找效率的关键,可以使用递推的方式产生部分匹配表;
    2.部分匹配值定义为前缀和后缀最长共有元素的长度;
    3.KMP利用部分匹配值与子串移动位数的关系提高查找效率。

    4.KMP算法的应用

    4.1.字符串类中的新功能

    数据结构(09)_字符串类的实现
    1.子串查找(kmp算法的直接应用)

    int indexOf(const char* s) const
    int indexOf(const String& s) const

    2.在字符串中将指定的字符串删除
    根据kmp在目标字符串中查找子串的位置,通过子串位置和长度进行删除。

    String& remove(const char* s)
    String& remove(const String& s)

    3.字符串的减法操作定义(opeator -)
    使用remove实现字符串间的减法操作,注意:字符串本身不被修改,返回产生的新串。
    4.字符串中的子串替换
    Eg:在字符串”abcdefg”中将”cd”替换为”xyz” ?
    1.使用kmp/indexOf获取cd所在的位置
    2.使用remove()删除字符串“cd”;
    3.将字符串”xyz”,使用insert插入1中获取的位置。

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

    5.从字符串创建子串
    以i为起点提取长度为len的子串,子串提取不会改变原来字符串的状态。
    方法:从字符串的i位置开始拷贝长度为len的子串出来
    String sub(int i, int len) const

    4.2. 字符串类的最终实现

void String::init(const char* s)
{
    m_str = strdup(s ? s : "");

    if(m_str != NULL)
    {
        m_length = strlen(m_str);
    }
    else
    {
        THROW_EXCEPTION(NoEnoughMemoryException, "no memory to dup string object...");
    }
}

bool String::equal(const char* l, const char* r, int len) const
{
    bool ret = true;

    for(int i=0; i<len && ret; i++)
    {
        ret = (l[i] == r[i]);
    }

    return ret;
}

String::String()
{
    init("");
}
String::String(char c)
{
    char s[] = {c, '\0'};
    init(s);
}
String::String(const char* s)
{
    init(s ? s : "");
}
String::String(const String& s)
{
    init(s.m_str);
}

int String::length() const
{
    return m_length;
}
const char* String::str() const
{
    return m_str;
}

bool String::startWith(const char* s) const
{
    bool ret = (s != NULL);

    if(ret)
    {
        int len = strlen(s);
        ret = (len < m_length) && equal(m_str, s, len);
    }

    return ret;
}
bool String::startWith(const String& s) const
{
    return startWith(s.m_str);
}
bool String::endOf(const char* s) const
{
    bool ret = (s != NULL);

    if(ret)
    {
        int len = strlen(s);
        char* str = m_str + m_length - len;
        ret = (len < m_length) && equal(str, s, len);
    }

    return ret;
}
bool String::endOf(const String& s) const
{
    return endOf(s.m_str);
}

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);
}

String& String::insert(int i,const char* s)
{
    if((0 <= i) && (i <= m_length))
    {
        if((s != NULL) && (s[0] != '\0'))
        {
            int len = strlen(s);
            char* str = reinterpret_cast<char*>(malloc(m_length + len + 1));

            if(str !=NULL)
            {
                strncpy(str, m_str, i);
                strncpy(str + i, s, len);
                strncpy(str + i + len, m_str + i, m_length - i);
                str[m_length + len] = '\0';

                free(m_str);

                m_str = str;
                m_length = m_length + len;
            }
            else
            {
                THROW_EXCEPTION(NoEnoughMemoryException,"No memory to creat String object ...");
            }
        }
    }
    else
    {
        THROW_EXCEPTION(IndexOutOfBoundsException,"Parameter i is invalid ...");
    }
    return *this;
}
String& String::insert(int i, const String& s)
{
    return insert(i, s.m_str);
}

String& String::remove(int i, int len)
{
    if( (i<=0) && (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)
{
    int index = kmp(m_str, (s ? s : ""));
    int len = strlen(s ? s : "");

    return remove(index, len);
}
String& String::remove(const String& s)
{
    int index = kmp(m_str, s.m_str);
    int len = strlen(s.m_str);

    return remove(index, len);
}

String& String::replace(const char* t, const char* s)
{
/*  1.使用kmp/indexOf获取cd所在的位置
    2.使用remove()删除字符串“cd”;
    3.将字符串”xyz”,使用insert插入1中获取的位置。*/

    int index = indexOf(t);

    if(index >=0)
    {
        remove(t);
        insert(index,s);
    }

    return *this;
}
String& String::replace(const String& t, const String& s)
{
    return replace(t.m_str, s.m_str);
}
String& String::replace(const char* t, const String& s)
{
    return replace(t, s.m_str);
}
String& String::replace(const String& t, const char* s)
{
    return replace(t.m_str, s);
}

String& String::trim()
{
    int b = 0;
    int e = m_length - 1;

    while(m_str[b] == ' ') b++;
    while(m_str[e] == ' ') e--;

    if(b == 0)
    {
        m_str[e + 1] = '\0';
        m_length = e + 1;
    }
    else
    {
        for(int i=0, j=b; j<=e; i++, j++)   //注意j<=e
        {
            m_str[i] = m_str[j];
        }
        m_str[e - b + 1] = '\0';
        m_length = e - b + 1;
    }

    return *this;
}

String String::sub(int i,int len) const
{
    String ret;

    if((0<=i)&&(i<m_length))
    {
        if(len<0) len = 0;
        if(len+i>m_length) len = m_length - i;

        char* str = reinterpret_cast<char*>(malloc(len + 1));

        strncpy(str,m_str + i,len);
        str[len] = '\0';
        ret = str;
    }
    else
    {
        THROW_EXCEPTION(IndexOutOfBoundsException,"Parameter i is invalid ...");
    }
    return ret;
}

char& String::operator [] (int i)
{
    if( (0<=i) && (i<m_length))
    {
        return m_str[i];
    }
    else
    {
        THROW_EXCEPTION(IndexOutOfBoundsException, "index out of range...");
    }
}
char& String::operator [] (int i) const
{
    return (const_cast<String&>(*this))[i];
}

bool String::operator ==(const String& s) const
{
    return (strcmp(m_str, s.m_str) == 0);
}
bool String::operator ==(const char* s) const
{
    return (strcmp(m_str, (s ? s : "")) == 0);
}

bool String::operator !=(const String& s) const
{
    return !(*this == s.m_str);
}
bool String::operator !=(const char* s) const
{
    return !(*this == (s ? s : ""));
}

bool String::operator >(const String& s) const
{
    return (strcmp(m_str, s.m_str) > 0);
}

bool String::operator >(const char* s) const
{
    return (strcmp(m_str, (s ? s : "")) > 0);
}

bool String::operator <(const String& s) const
{
    return (strcmp(m_str, s.m_str) < 0);
}
bool String::operator <(const char* s) const
{
    return (strcmp(m_str, (s ? s : "")) < 0);
}

bool String::operator >=(const String& s) const
{
    return (strcmp(m_str, s.m_str) >= 0);
}
bool String::operator >=(const char* s) const
{
    return (strcmp(m_str, (s ? s : "")) >= 0);
}

bool String::operator <=(const String& s) const
{
    return (strcmp(m_str, s.m_str) <= 0);
}
bool String::operator <=(const char* s) const
{
    return (strcmp(m_str, (s ? s : "")) <= 0);
}

String String::operator +(const String& s) const
{
    return (*this + s.m_str);
}
String String::operator +(const char* s) const
{
    String ret;
    int len = strlen(s ? s : "") + m_length;
    char* str = reinterpret_cast<char*>(malloc(len + 1));

    if(str != NULL)
    {
        strcpy(str, m_str);
        strcat(str, (s ? s : ""));

        free(ret.m_str);

        ret.m_str = str;
        ret.m_length = len;
    }
    else
    {
        THROW_EXCEPTION(NoEnoughMemoryException, "no memory to create string object...");
    }

    return ret;
}
String& String::operator +=(const String& s)
{
    return (*this = (*this + s.m_str));
}
String& String::operator +=(const char* s)
{
    return (*this = (*this + s));
}

String& String::operator =(const String& s)
{
    return (*this = s.m_str);
}
String& String::operator =(const char* s)
{
    if(m_str != s)
    {
        char* str = strdup(s ? s : "");

        if(str != NULL)
        {
            free(m_str);
            m_str = str;
            m_length = strlen(m_str);
        }
        else
        {
            THROW_EXCEPTION(NoEnoughMemoryException, "no memory to create string object...");
        }
    }

    return *this;
}
String& String::operator =(char c)
{
    char s[] = {c, '\0'};
    return (*this = s);
}

int* String::make_pmt(const char* p)
{
    int len = strlen(p);
    int* ret = static_cast<int*>(malloc(sizeof(int) * len));

    if(ret != NULL)
    {
        int ll = 0;
        ret[0] = 0;

        for(int i=1;i<len;i++)
        {
            while((ll>0) && (p[ll] != p[i]))
            {
                ll = ret[ll];
            }

            if(p[ll] == p[i])
            {
                ll++;
            }
            ret[i] = ll;
        }
    }
    return ret;
}

int String::kmp(const char*s,const char* p)
{
    int ret = -1;
    int sl = strlen(s);
    int pl = strlen(p);
    int* pmt = make_pmt(p);

    if((pmt != NULL) && (0<pl) && (pl <= sl))
    {
        for(int i=0,j=0;i<sl;i++)
        {
            while((j>0) && (s[i] != p[j]))
            {
                j = pmt[j -1];
            }

            if(s[i] == p[j])
            {
                j++;
            }

            if(j==pl)
            {
                ret = i + 1 - pl;
                break;
            }
        }
    }

    free(pmt);
    return  ret;
}

String::~String()
{
    free(m_str);
}

4.3.总结:

字符串类是工程开发中必不可少的组件,应当包含常用的增删查改操作;
增 : insert, operator +, ...
删 : remove, operator -, ...
查 : indexOf, ...
改 : replace, ...

转载于:https://blog.51cto.com/11134889/2133693

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值