private void getNextval(StringList T, int[] nextval) {
int i, j; //代表第几个字符
i = 1;
j = 0;
nextval[1] = 0;
for (i=2; i < nextval.Length; i++)
{
while (j > 0 && T[i] != T[j + 1])
{
j = nextval[j];
}
if (T[i] == T[j + 1])
{
j++;
}
nextval[i] = j;
}
}这里计算的就是匹配串的前index(包括index)的字符串的最长前后缀相同的字符个数
例如:ababaaaba,那么
"a",next[1] = 0
"ab",next[2] = 0
"aba",next[3] = 1
"abab",next[4] = 2
"ababa",next[5] = 3
"ababaa",next[6] = 1
"ababaaa",next[7] = 1
"ababaaab",next[8] = 2
"ababaaaba",next[9] = 3
这个数组的作用就是,比如说:你在主串ababatabssss中匹配ababaaaba,你会发现前面5个字符都是一样的,就是第5+1个不一样
普通的匹配模式,主串会从第二个开始ababaaabssss,而匹配串从头开始ababaaaba
但是因为有next数组的存在,next[5]的存在,匹配串回到next[5]=3,然后主串位置不变,比较第3+1位
ababatabssss
abababaaaba
这样就可以减少很多的匹配次数了
KMP匹配算法主要是利用匹配失败后的信息来减少匹配的次数。
首先是先对匹配串的自我分析,也就是nextvalue数组的求解,里面求出的是下一个应该匹配的位置。
然后就可以将主串不断向后推移,匹配串不断选取合适的位置来和主串推移的位置进行比较。
//普通匹配查找串的位置
public int SimpleIndex(StringList T, int pos = 1) {
if (T.isEmpty() || this.isEmpty()) {
return 0;
}
int mainLength = this.Length();
int matchLength = T.Length();
int i;
if (pos > 0 && pos <= mainLength) {
i = pos;
while (i <= mainLength - matchLength + 1) {
StringList sub = this.SubString(i, matchLength);
if (sub.Compare(T) != 0)
{
i++;
}
else {
return i;
}
}
}
return 0;
}一步步截取匹配长度的子串和匹配串对比。并不效率!



//比较两个串 1本串大 -1 s串大 0相等
public int Compare(StringList s) {
int sounceLength = this.Length();
int length = s.Length();
int i = 0;
int j = 0;
char c = '\0';
while (i < sounceLength && j < length) {
s.GetCharByIndex(j + 1, ref c);
if (this._data[i] > c) {
return 1;
}
if (this._data[i] < c) {
return -1;
}
i++;
j++;
}
if (sounceLength == length)
{
return 0;
}
if (sounceLength > length) {
return 1;
}
return -1;
}//获取子字符数组
private char[] subChar(char[] chars, int index, int length) {
if (chars == null) {
Console.WriteLine("截取的字符数组为空!");
return null;
}
int cLength = chars.Length;
if (length <1 || index < 1||index > cLength || index + length - 1 > cLength) {
Console.WriteLine("索引和长度错误!");
return null;
}
char[] newChar = new char[length];
for (int i = 0; i < length; i++) {
newChar[i] = chars[index - 1 + i];
}
return newChar;
}
//截取指定位置长度的串,成为一个新的串
public StringList SubString(int index, int length) {
char[] sub = this.subChar(this._data, index, length);
if (sub == null) {
return null;
}
StringList newString = new StringList(sub);
return newString;
}
//删除指定位置长度的字符串
public bool Delete(int index, int length) {
if (this.isEmpty()) {
Console.WriteLine("被删除的字符串为空!");
return false;
}
int sounceLength = this._data.Length;
if (index < 1 || index > sounceLength || length < 1 || index + length - 1 > sounceLength) {
Console.WriteLine("索引位置和长度错误!");
return false;
}
char[] term = this._data;
int newLength = sounceLength - length;
this._data = new char[newLength];
int go = 0;
for (int i = 0; i < newLength; i++) {
if (i == index - 1) {
go = length;
}
this._data[i] = term[i + go];
}
return true;
}①同样的需要判断串是否为空和索引的合法性
②临时保存原串的数据
③重构数据域大小为删除字符串后的长度
④遍历赋值,通过go位移跳过被删除的元素

//插入字符数组
public bool Insert(int index , char[] chars)
{
if (this.isEmpty()||chars == null) {
Console.WriteLine("插入的字符不能为空!");
return false;
}
int length = _data.Length;
if (index > length || index < 1) {
Console.WriteLine("索引位置错误!");
return false;
}
int cLength = chars.Length;
char[] term = _data;
_data = new char[length + cLength];
int i = 0;
for (; i < index - 1; i++) {
_data[i] = term[i];
}
for (int j = 0; j < cLength; j++) {
_data[i] = chars[j];
i++;
}
for (int k = index - 1; k < length; k++) {
_data[i] = term[k];
i++;
}
return true;
}①先判断本串和字符数组是否为空
②判断索引的合法性
③临时保存本串的数据
④重构本串的数据域大小为原串长度+插入字符数组长度
⑤原串index前的字符,插入的字符,原串index和其后的字符,分为三段分别按顺序赋值给新的数据域

串其实解释我们平常用的string字符串,它也是一种线性表结构,不同的是,普通的线性表一般都是针对单个元素的,而串一般针对的是多个元素的操作。
//串的顺序存储结构
class StringList
{
private char[] _data;
public StringList(char[] chars) {
this.ValueTo(chars);
}
//创建空串
public StringList() {
this._data = null;
}
//串赋值为字符数组
public void ValueTo(char[] chars) {
if (chars == null) {
this._data = null;
return;
}
this._data = new char[chars.Length];
for (int i = 0; i < chars.Length; i++)
{
_data[i] = chars[i];
}
}
}可以看出,这里的串的数据域就是一个字符数组,数组的长度就是串的长度,因为c#的数组是可以重新定义大小的,所以分配到刚好的大小就好,也不用使用特殊符号来判定串的结束
有些语言的数组是不能重新定义大小的,所以扩大时会创建新的string(新的大小),缩小时以'\0'作为字符串的结束标志。
队列的链式存储结构很简单,需要两个变量指标来指向队头和队尾,这里的队尾是真实的队尾元素,用来添加元素。所以当队列只有一个元素时,队头和队尾指向的都是同一个元素。
分两种情况
①就是队尾rear指针大于队头指针front,length = rear - front

②队尾rear指针小于队头指针front
length1 = 数组长度 - front
length2 = rear
length = length1 + length2 = rear - front + 数组长度

③可以总结出
lenght = (rear - front + 数组长度)% 数组长度
//获取循环队列长度
public int GetLength() {
int maxLength = this._maxLength + 1;
return (this._rear - this._front + maxLength) % maxLength;
}