String的概念
String:
因为C++兼容C,所以我们的字符串类也要,不仅String可以使用,还要兼容Char*
通过C语言中的函数实现String类,这样才可以实现兼容不同版本的C++编译器
实现String类时,需要考虑字符串的const属性,函数参数是否为const
String类的功能:
函数名 | 功能 |
---|---|
insert | 增 ,返回引用 |
remove | 删,返回引用 |
replace | 改,返回引用 |
重载[] | 像C语言一样访问字符串的内容(与List一致必须先有内容才可以访问或修改) |
重载比较操作符 | 进行字符串的比较 |
重载+、+=、-、-= | 字符串的便捷操作,返回引用 |
trim() | 消除前后两端空格,返回引用 |
bool startWith() | 以…开始 |
bool endOf() | 以…结束 |
int indexOf() | 子串位于当前字符串的下标(KMP算法) |
String sub(int i,int len) | 从字符串中提取出子串 |
Tip:返回为引用时可以继续修改或者操作,改变的就是原字符串的内容
若不返回引用,返回的为临时变量,后续对临时变量的修改与原变量无关
String.h
#ifndef __String_H_
#define __String_H_
#include "Object.h"
namespace JYlib
{
/*
字符串类
功能:增、删、查、改
*/
class String : public Object
{
protected:
char* m_str;
int m_length;
void init(const char* s);
bool equal(const char* l,const char* r,int len)const;
//kmp子串查找算法
static int* make_pmt(const char* s);
static int kmp(const char* s,const char* p);
public:
String();
String(char s);
String(const char* s);
String(const String& s);
int length()const;
const char* str()const;
String& insert(int i,const char* s);
String& insert(int i,const String& s);
String& trim();//消除前后两端空格
String sub(int i,int len)const;//提取子串
//子串位于当前类的下标
int indexOf(const char* s)const;
int indexOf(const String& s)const;
String& remove(int i,int len);
String& remove(const char* s);
String& remove(const String& s);
//将字符串中的t替换为s
String& replace (const char* t,const char* s);
String& replace(const char* t,const String& s);
String& replace(const String& t,const char* s);
String& replace(const String& t,const String& s);
char& operator[](int i);
char operator[](int i)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;
String& operator = (const char c);
String& operator = (const char* s);
String& operator = (const String& s);
String operator + (const char* s)const;
String operator + (const String& s)const;
String& operator += (const char* s);
String& operator += (const String& s);
String operator - (const char* s)const;
String operator - (const String& s)const;
String& operator -= (const char* s);
String& operator -= (const String& s);
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;
bool operator <= (const String& s)const;
~String();
};
}
#endif
String.cpp
#include "String.h"
#include <cstring>
#include <cstdlib>
#include "Exception.h"
using namespace JYlib;
void String::init(const char* s)
{
m_str = strdup(s);
if(m_str != NULL)
{
m_length = strlen(m_str);
}
else
{
THROW_EXCEPTION(NoEnoughMemoryException,"No memory to creat String object ...");
}
}
bool String::equal(const char* l,const char* r,int len)const
{
for(int i=0;i<len;i++)
{
if(l[i]!=r[i])
{
return false;
}
}
return true;
}
/* KMP子串查找算法——生成PMT表 */
int* String::make_pmt(const char* s)
{
int len = strlen(s);
int* ret = reinterpret_cast<int*>(malloc(len * sizeof(int)));
if(ret)
{
int ll = 0;//前缀与后缀的最大重叠长度
ret[0] = 0;
for(int i=1;i<len;i++)
{
while((ll > 0)&&(s[ll] != s[i]))
{
ll = ret[ll -1];
}
if(s[ll] == s[i])
{
ll++;
}
ret[i] = ll;
}
}
else
{
THROW_EXCEPTION(NoEnoughMemoryException,"No memory to creat pmt array");
}
return ret;
}
/* KMP子串查找算法
在目标串s中,查找子串p,生成PMT表需要耗费一定时间
适合用于目标串较子串很长,子串重复性低的场合
*/
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()
{
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 ? s.m_str : "");
}
int String::length()const
{
return m_length;
}
const char* String::str()const
{
return 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* new_str = reinterpret_cast<char*>(malloc(m_length + len + 1));
if(new_str)
{
strncpy(new_str,m_str,i);
strncpy(new_str + i,s,len);
strncpy(new_str + i + len,m_str + i,m_length - i);
m_length += len;
new_str[m_length] = '\0';
free(m_str);
m_str = new_str;
return *this;
}
else
{
THROW_EXCEPTION(NoEnoughMemoryException,"No memory to insert String value ...");
}
}
}
else
{
THROW_EXCEPTION(IndexOutOfBoundsException,"Paramemter is invalid ...");
}
}
String& String::insert(int i,const String& s)
{
return insert(i,s.m_str);
}
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++)
{
m_str[i] = m_str[j];
}
m_length = e - b + 1;
m_str[m_length] = '\0';
}
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* new_str = reinterpret_cast<char*>(malloc(len + 1));
strncpy(new_str,m_str + i,len);
new_str[len] = '\0';
ret = new_str;
}
else
{
THROW_EXCEPTION(IndexOutOfBoundsException,"Paramemter is invalid ...");
}
return ret;
}
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::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];
n++;
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.m_length);
}
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 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::replace(const String& t,const String& s)
{
return replace(t.m_str,s.m_str);
}
char& String::operator[](int i)
{
if((0 <= i)&&(i < m_length))
{
return m_str[i];
}
else
{
THROW_EXCEPTION(IndexOutOfBoundsException,"Parameter i is invalid ...");
}
}
char String::operator[](int i)const
{
return const_cast<String&>(*this)[i];
}
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);
}
String& String::operator = (const char* s)
{
if(m_str != s)
{
char* new_str = strdup(s ? s : "");
if(new_str)
{
free(m_str);
m_str = new_str;
m_length = strlen(m_str);
}
else
{
THROW_EXCEPTION(NoEnoughMemoryException,"No memory to creat new element ...");
}
}
return *this;
}
String& String::operator = (char c)
{
char s[]={c,'\0'};
return (*this = s);
}
String& String::operator = (const String& s)
{
return (*this = s.m_str);
}
String String::operator + (const char* s)const
{
String ret;
int len = m_length + strlen(s ? s : "");
char* new_str = reinterpret_cast<char*>(malloc(len+1));
if(new_str)
{
strcpy(new_str,m_str);
strcat(new_str,s ? s : "");
free(ret.m_str);
ret.m_str = new_str;
ret.m_length = len;
}
else
{
THROW_EXCEPTION(NoEnoughMemoryException,"No memory to creat new element ...");
}
return ret;
}
String String::operator + (const String& s)const
{
return (*this + s.m_str);
}
String& String::operator += (const char* s)
{
return (*this = *this + s);
}
String& String::operator += (const String& s)
{
return (*this = *this + s.m_str);
}
String String::operator - (const char* s)const
{
return String(*this).remove(s);
}
String String::operator - (const String& s)const
{
return String(*this).remove(s);
}
String& String::operator -= (const char* s)
{
return remove(s);
}
String& String::operator -= (const String& s)
{
return remove(s);
}
/*----------------比较操作符----------------*/
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);
}
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);
}
/*----------------比较操作符----------------*/
String::~String()
{
free(m_str);
}
KMP算法原理
![](https://img-blog.csdnimg.cn/2020031421473491.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0VzX1N0dWR5X1l1,size_16,color_FFFFFF,t_70)
从上图可以发现:
匹配失败时,右移位数与子串本身相关,与目标串无关
移动位数 = 已匹配的字符数 - 对应的部分匹配值
任意子串都存在一个唯一的部分匹配表
部分匹配表的相关概念:
前缀:除了最后—个字符以外,一个字符串的全部头部组合
后缀:除了第—个字符以外,—个字符串的全部尾部组合
部分匹配值:前缀和后缀最长共有元素的长度
以ABCDABD为例:
![](https://img-blog.csdnimg.cn/20200314220553473.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0VzX1N0dWR5X1l1,size_16,color_FFFFFF,t_70)
![](https://img-blog.csdnimg.cn/20200314214200363.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0VzX1N0dWR5X1l1,size_16,color_FFFFFF,t_70)
实现关键:
PMT[1] = 0 (下标为0的元素匹配值为0)
从2个字符开始递推(从下标为1的字符开始递推)
假设 PMT[n] = PMT[n-1] + 1 (最长共有元素的长度)
当假设不成立,PMT[n]在PMT[n-1]的基础上减小
KMP.h
#ifndef __KMP_H_
#define __KMP_H_
#include <cstdlib>
#include <cstring>
#include "Exception.h"
namespace JYlib
{
/*
KMP子串查找算法——生成PMT表
*/
int* make_pmt(const char* s)
{
int len = strlen(s);
int* ret = reinterpret_cast<int*>(malloc(len * sizeof(int)));
if(ret)
{
int ll = 0;//前缀与后缀的最大重叠长度
ret[0] = 0;
for(int i=1;i<len;i++)
{
while((ll > 0)&&(s[ll] != s[i]))
{
ll = ret[ll -1];
}
if(s[ll] == s[i])
{
ll++;
}
ret[i] = ll;
}
}
else
{
THROW_EXCEPTION(NoEnoughMemoryException,"No memory to creat pmt array");
}
return ret;
}
/*
KMP子串查找算法
在目标串s中,查找子串p,生成PMT表需要耗费一定时间
适合用于目标串较子串很长,子串重复性低的场合
*/
int 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;
}
}
#endif