字符串(String)
1. 字符串的概念
1.1 字符串的定义
- 字符串,是一串文字和符号的序列,是由零个或多个字符的顺序排列所组成的数据结构,其基本组成元素是单个字符(char),字符串的长度可变。
- 字符串简称为串(String),是n(n≥0)个字符的一个有限序列。通常可记为S=” a0a1a2……an−1 ”,其中,S是串名,可以是串变量名,也可以是串常量名。
- 用引号‘…’或“…”作为分界符括起来的叫做串值,其内的 ai 是串中的字符(0 ≤ i< n),n是串中的字符个数,也叫作串的长度,它不包括作为分界符的引号,也不包括串结束符‘\0’。
1.2 字符串的存储表示
- 字符串是一种特殊的线性结构,其存储表示有两种:数组存储表示和链接存储表示。
- 顺序串:字符串的数组存储表示简称为顺序串。
(1)它用一组地址连续的存储单元来存储字符串中的字符序列。
(2)可以使用定长的字符数组来实现。
(3)如果使用C语言中的类型定义,顺序串的存储分配可以分为两种:静态分配的数组表示和动态分配的数组表示。 - 链串:串的块链存储的组织形式与一般的单链表类似,主要区别在于,串中的一个存储结点可以存储多个字符。
(1)通常将链串中每个存储结点所存储的字符个数成为结点大小。
(2)当结点大小大于1时,串的最后一个结点的各个数据域不一定总能全部被字符占满,此时,应在这些未占用的数据域里不上不属于字符集的特殊字符(如“#”)以示区别。
(3)结点大小为1时存储密度低但操作方便,而结点大小大于1时存储密度高但操作不方便,具体如下图所示:
1.3 空串和空白串的区别
- 空串:长度为零的串叫做空串,除串结束符外,它不包含任何其他字符。
- 空白串:空白串的长度不为零,除串结束符外,它包含的其他字符均为空格。
2. 字符串的模式匹配
- 模式匹配(Pattern matching):查找模式串在目标串中的匹配位置的运算。
- 字符串的模式匹配问题描述:设有两个字符串T和pat,若打算在串T中查找是否有与串pat相等的子串,则称串T为目标(Target),称串pat为模式(Pattern)。
2.1 朴素的模式匹配——B-F算法(由Brute和Force提出,一种带回溯的字符串匹配算法)
- 基本想法:用串pat的字符依次与串T中的字符做比较。
- 算法原理如下:
(1)如果 t0 = p0 , t1 = p1 …… tm−1 = pm−1 ,则匹配成功,返回模式串pat第0个字符 p0 在目标串T中匹配的位置。
(2)如果在其中某个位置i, ti ≠ pi ,比较不等,这时将模式串pat右移一位,用pat中字符从头开始与T中字符依次比较,如此反复执行,直到得出结果:结果一,执行到某一趟,模式串的所有字符都与目标串对应字符相等,则匹配成功,结束算法;结果二,pat已经移到最后可能与T比较的位置,但不是每一个字符都能与T匹配,则匹配失败,结束算法。
2.2 模式匹配的改进算法——KMP算法(由D.E.Knuth、J.H.Morris和V.R.Pratt提出,一种无回溯的字符串匹配算法,更高效)
- 基本想法:引入一个跳转表next[],跳过不必要的比较过程,避免回溯,提高效率。
- 算法原理如下:
(1)用一个next特征函数来确定,当模式P中第j个字符与目标S中相应字符失配时,模式P中应当由哪个字符(设为第k+1个)与目标中刚失配的字符重新继续进行比较。
(2)设模式P= p0p1……pj−2pj−1 ,则它的next特征函数定义如下:
3. 字符串相关库函数
3.1 C++有关字符串的库函数
- C++提供的有关字符串的函数库的名字为 < <script type="math/tex" id="MathJax-Element-15"><</script>string.h > ,其具体的字符串操作方法说明如下:http://blog.csdn.net/cainv89/article/details/47703949
3.2 STL有关字符串的类库
- STL提供的有关字符串的类库的名字为
< string > <script type="math/tex" id="MathJax-Element-18">></script>,其具体的字符串操作方法说明如下:http://blog.csdn.net/cainv89/article/details/47208019
4. 字符串的实现
4.1 字符串的自定义类
文件:AString.h
#ifndef ASTRING_H_ #define ASTRING_H_ #include <iostream> #include <string.h> //#include <strstream> using namespace std; const int defaultSize = 128; class AString { public: AString(int sz = defaultSize); //构造函数,构造一个最大长度为sz,实际长度为0的字符串 AString(const char *init); //构造函数,构造一个最大长度为maxSize,由init初始化的新字符串对象 AString(const AString& obj); //拷贝构造函数,由一个已有的字符串对象ogj构造一个新字符串 ~AString(); //析构函数,释放动态分配的串空间并撤销改字符串对象 public: int Length()const; //函数返回串*this的实际长度 public: AString operator()(int pos, int len); //当0<=pos<maxSize且0<=len且pos+len<maxSize时,则在串*this中从pos所指出位置开始连续取len个字符组成子串返回 public: int operator==(AString& obj)const; //判是否串相等,若串*this与obj相等,则函数返回1,否则函数返回0 int operator!=(AString& obj)const; //判是否串不相等,若串*this与obj不相等,则函数返回1,否则函数返回0 public: int operator!()const; //判是否串空,若串*this为空,则函数返回1,否则函数返回0 public: AString& operator=(AString& obj); //串obj赋值给当前串*this public: AString& operator+=(AString& obj); //若length(*this)+length(obj)<maxSize,则把串obj接在串*this后面 public: char& operator[](int i); //取*this的第i个字符 public: //若串pat与串*this中的某个子串匹配,则函数返回第1次匹配时子串在串*this中的位置,若串pat为空或在串*this中没有匹配子串,则函数返回-1 int Find(AString* pat)const; //B-F算法,朴素的模式匹配,带回溯的字符串匹配算法 int fastFind(AString* pat, int next[])const; //KMP算法,模式匹配的改进算法,无回溯的字符串匹配算法 public: void getNext(int next[]); //计算next[]的算法 private: char *ch; //串存放数组 int curLength; //串的实际长度 int maxSize; //存放数组的最大长度 }; #endif /*ASTRING_H_*/
4.2 字符串操作的实现
文件:AString.cpp
#include "AString.h" //构造函数,构造一个最大长度为sz,实际长度为0的字符串 AString::AString(int sz) { if (sz >= 0) { maxSize = sz; curLength = 0; ch = new char[maxSize + 1]; ch[0] = '\0'; } } //构造函数,构造一个最大长度为maxSize,由data初始化的新字符串对象 AString::AString(const char *init) { int len = strlen(init); maxSize = (len > defaultSize) ? len : defaultSize; curLength = len; ch = new char[maxSize + 1]; strcpy(ch, init); } //拷贝构造函数,由一个已有的字符串对象ogj构造一个新字符串 AString::AString(const AString& obj) { maxSize = obj.maxSize; curLength = obj.curLength; ch = new char[maxSize + 1]; strcpy(ch, obj.ch); } //析构函数,释放动态分配的串空间并撤销改字符串对象 AString::~AString() { delete[] ch; ch = NULL; } //函数返回串*this的实际长度 int AString::Length()const { return curLength; } //当0<=pos<maxSize且0<=len且pos+len<maxSize时,则在串*this中从pos所指出位置开始连续取len个字符组成子串返回 AString AString::operator()(int pos, int len) { AString temp; if ((pos >= 0) && (pos < maxSize) && (len >= 0) && ((pos + len) < maxSize)) { len = ((pos + len - 1) >= curLength) ? (curLength - pos) : len; temp.curLength = len; for (int i = 0, j = pos;i < len;i++, j++) { temp.ch[i] = ch[j]; } temp.ch[len] = '\0'; } return temp; } //判是否串相等,若串*this与obj相等,则函数返回1,否则函数返回0 int AString::operator==(AString& obj)const { return (strcmp(ch, obj.ch) == 0) ? 1 : 0; } //判是否串不相等,若串*this与obj不相等,则函数返回1,否则函数返回0 int AString::operator!=(AString& obj)const { return (strcmp(ch, obj.ch) != 0) ? 1 : 0; } //判是否串空,若串*this为空,则函数返回1,否则函数返回0 int AString::operator!()const { return (curLength == 0) ? 1 : 0; } //串obj赋值给当前串*this AString& AString::operator=(AString& obj) { //两个串相等为自我赋值,否则字符串自身赋值出错 if (&obj != this) { delete[] ch; maxSize = obj.maxSize; curLength = obj.curLength; ch = new char[maxSize + 1]; strcpy(ch, obj.ch); } return *this; } //若length(*this)+length(obj)<maxSize,则把串obj接在串*this后面 AString& AString::operator+=(AString& obj) { char *temp = ch; curLength += obj.curLength; maxSize = (maxSize >= curLength) ? maxSize : curLength; delete[] ch; ch = new char[maxSize+1]; strcpy(ch, temp); strcat(ch, obj.ch); delete[] temp; return *this; } //取*this的第i个字符 char& AString::operator[](int i) { if ((i < 0) || (i >= curLength)) { exit(1); } return ch[i]; } //若串pat与串*this中的某个子串匹配,则函数返回第1次匹配时子串在串*this中的位置,若串pat为空或在串*this中没有匹配子串,则函数返回-1 //B-F算法,朴素的模式匹配,带回溯的字符串匹配算法 int AString::Find(AString* pat)const { int i = 0; int j = 0; for (i = 0;i <= curLength - pat->curLength;i++) { for (j = 0;j < pat->curLength;j++) { if (ch[i + j] != pat->ch[j]) { break; } } if (j == pat->curLength) { return i; } } return -1; } //KMP算法,模式匹配的改进算法,无回溯的字符串匹配算法 int AString::fastFind(AString* pat, int next[])const { int posP = 0; //模式串的扫描指针 int posT = 0; //目标串的扫描指针 int lengthP = pat->curLength; //模式串的长度 int lengthT = curLength; //目标串的长度 while ((posP < lengthP) && (posT < lengthT)) //对两串扫描 { if ((posP == -1) || (pat->ch[posP] == ch[posT])) //对应字符匹配 { posP++; posT++; } else { posP = next[posP]; } } if (posP < lengthP) { return -1; } else { return posT - lengthP; } return -1; } //计算next[]的算法 void AString::getNext(int next[]) { int j = 0; int k = -1; next[0] = -1; while (j < curLength) { if ((k == -1) || (ch[j] == ch[k])) { j++; k++; next[j] = k; } else { k = next[k]; } } }
参考文献:
[1]《数据结构(用面向对象方法与C++语言描述)(第2版)》殷人昆——第四章
[2] 百度搜索关键字:字符串,KMP算法,跳转表