数据结构(09)_字符串类的实现
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构(09)_字符串类的实现相关的知识,希望对你有一定的参考价值。
1.字符串类的创建(上)
1.1.历史遗留问题:
C语言不支持真正意义上的字符串,使用字符指针和字符数组实现字符串存储,使用C库函数实现字符串操作。
C++为了兼容C语言,也不支持原生的字符串类型,但可以通过自定义类类型完成字符串类型的定义。
1.2.DTstring的设计
继承自顶层父类Object,内部封装C语言中的字符串操作。
1.无缝实现String对象和char*字符串的互操作;
2.操作符重载函数需要考虑是否支持const版本
3.通过C语言中的字符串函数实现String的成员函数
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.字符串类的创建(下)
字符串类中的常用成员函数:
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;
3.在指定位置插入字符串
String& insert(int i, const char* s);
String& insert(int i, const String& s);
4.去掉字符串两端的空白字符串
String& trim();
思考:如何在目标字符串中查找是否存在指定的子串?
3.KMP子串查找算法
问题:如何在目标字符串中查找是否存在指定的子串?
3.1.朴素解法
先比对子串和目标字符串的第一个字符,如果匹配则继续匹配第二个字符...
如果匹配失败,则从目标字符串的第二个字符与子串进行匹配, ...
通过两个嵌套的for循环完成,最终的时间复杂度为O(n^2)。
3.2.KMP子串查找法
朴素解法的一个优化方向:
因为, Pa != Pb,且Pb = Sb;所以Pa != Sb,结论,子串p右移1位比较没有意义。
伟大的发现:
1.匹配失败时的右移位数与子串本身相关,与目标串无关;
2.移动位数 = 已知匹配的字符数 - 对应的部分匹配值
3.任意一个子串都存在一个唯一的部分匹配表。
3.2.1.部分匹配表
那么部分匹配表是如何得到的呢?
- 前缀:除了最后一个字符外,一个字符串的全部头部组合;
- 后缀:除了最后一个字符外,一个字符串的全部尾部组合;
- 部分匹配值:前缀和后缀最长共有元素的长度。
下面以ABCDABD为例:
上面我们使用求前缀和后缀交集最大长度的方法得到了部分匹配值,但如何通过编程来产生部分匹配表呢?
实现关键:
1.PMT[1] = 0(下标为1的字符开始递推)
2.从第二个字符开始递推(从下标为1的字符开始递推)
3.假设PMT[n] = PMT[n-1]+1(最长共有元素的长度)
4.当假设不成立,PMT[n]在PMT[n-1]的基础上减小,直至为0
总结:
1.部分匹配表是提高子串查找效率的关键,可以使用递推的方式产生部分匹配表;
2.部分匹配值定义为前缀和后缀最长共有元素的长度;
3.KMP利用部分匹配值与子串移动位数的关系提高查找效率。4.KMP算法的应用
4.1.字符串类中的新功能
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, ‘ ‘};
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] != ‘ ‘))
{
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] = ‘ ‘;
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] = ‘ ‘;
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] = ‘ ‘;
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] = ‘ ‘;
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] = ‘ ‘;
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, ‘ ‘};
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, ...
以上是关于数据结构(09)_字符串类的实现的主要内容,如果未能解决你的问题,请参考以下文章