字符串的处理和算法分析
2015-06-02 22:57
267 查看
《字符串的处理和算法分析》
一.基本概念:
1.数据类型:
(1).值类型:表示该数据类型存储是一个数据值,基于值类型的变量直接包含指。
a.简单数据类型,b.结构类型,c.枚举类型
(2).值类型:(又名:对象)。
(3).指针类型:指向值类型或者引用类型的数据类型。
(注明:在java中string不是基本数据类型,但在C#中string是基本数据类型)
2.字符串定义:是仅有字符构成的有限序列,是取值范围受限的线性表。
(线性表定义:是由n(n>=0)个元素的有限序列)
3.字符串的存储:
(1).顺序存储:是指用一组地址连续的存储单元来存储船只的字符串序列。
(2).链式存储:当采用链表存储字符串的时候,每一个结点中可以存储一个字符,也可以存储多个字符。
(链式存储:是用结点来存储数据元素,(结点如下))
二.字符串操作:
1.字符串的赋值:字符串的默认值为null。
注明:
(1).null注解:
(a.)本意是“空的;元素只有零的”意思。计算机中通常表示空值,无结果,或是空集合。
(b).NULL指针 :NULL指针是一个无类型指针,并且值为0.
(c).数据库中的NULL:Null在数据库中表示 不知道的数据,主要有3种意思:
1)知道数据存在,但不知道具体值.
2)不知道数据是否存在.
3)数据不存在.
(d).java和c#中的null是一个关键字,通常用来声明一个空引用,Objecto = null,表示o对象是一个空引用,如果引用null,就会抛出一个NullPointerException异常。
(2).对于常量,编译的时候就直接存储他们的子面值,而不是他们的引用,如string s=”a”+”b”+”c”;次字符串就只产生一个对象。在string内部是通过stringbuilder实现的。
(3).string pool:是对应在常量池中存储string常量的区域,相同的string常量在常量池中只创建一个。
(4).string是使用final来实现字符串的存储,在string对象创建之后,就不能再修改次对象中存储的字符串内容。
2.基本操作:
(1)字符访问(下标访问s[i])
(2)打散为字符数组(ToCharArray)
(3)截取子串(Substring)
(4)匹配索引 (indexof())
(5))大小写转换(ToUpper和ToLower)
(6).填充对齐(PadLeft和PadRight)
(7).截头去尾(Trim)
(8).插入和删除(Insert和Remove)
(9).替换字符(串)(Replace)
(10).分割为字符串数组(Split)——互逆操作:联合一个字符串静态方法Join(seperator,arr[])
(11).格式化(静态方法Format)
(12).连接成一个字符串(静态方法Concat、静态方法Join和实例方法StringBuilder.Append)
例1:模拟一个trim()方法,去掉字符串两端的空格?
(1).判断字符串的第一个位置是否为空格,如果是,则继续向下判断,直到不是空格为止,结尾处判断也是如此。
(2).当开始和结尾都判断不是空格时就获取字符串。
例2:将一个字符串进行反转,将字符串中指定的部分进行反转:
(1).回忆数据元素的反转。
(2).将字符串编程数组,对数组反转。
(3).将反转的数组编程字符串。
(4).只要将反转的部分的开始和结束为止作为参数传递即可。
例3:获取一个字符串在另一个字符串中出现的次数:
(1).定义一个计数器。
(2).获取字符第一次出啊先的位置。
(3)从第一次出现位置后剩余的字符串中继续获取字符出现的位置。
(4).当获取不到时,计数完成。
3.字符串的比较:
(1).“==”是比较地址。只要使用“=”运算符,就会调用Equals( )方法,的以Equals( )方法与“=”是等价的。String.Equals方法来优化比较过程该方法能自动地完成引用比较和值比较。
(2).String.Compare是一个比较灵活的比较方法,由于考虑到大小写或文化因素,就可以使用此方法。因为它有许多重载的形式,可以接受大小写或文化的参数,也支持子串比较。
(3).String.CompareOrdinal是对严格基于字符的序数值完成比较。它其实是使用一种简单的算法来比较2个字符串的Unicode值。如果第一个字符串小于第二个字符串,则返回小于0;如果是2个字符串相等,返回0;如果第一个字符串大于第二字符串,则返回大于0的值。
三.String与StringBuffer比较:
(1).string re=”a”+”b”;与StringBuffer re=new string().append(“a”).append(“b”);
前者的效率要高于后者,因为在”a”+”b”处理后,才会将值赋给re,之开辟了一次内存。
(2).public String getString(String s1,String s2){
Return s1+s2;
}
Public String getString(String s1,String s2){
Return new StringBuffer().append(s1).append(s2);
}
两者的效率相同,因为开辟一个内存段,再合并(或者叫扩展)内存
(3).String s=”s1”; s+=”s2”;
StringBuffer s=new StringBuffer().append(“s1”).append(“s2”)
前者的效率高于后者,因为string是不可变的对象,每次“+=”操作都会构造新的string对象。
(4).StringBuffer s=new StringBuffer();
For(int i=0;i<5000;i++){
S.append(“a”);
}
StringBuffer s=new StringBuffer(25000);
For(int i=0;i<5000;i++){
S.append(“a”)
}
前者的效率高于后者,因为StringBuffer内部实现的是char数组,默认初始化长度为16,每当字符串长度大于char数组长度的时候,就会构造更大的新数组,并将原先的数组内容复制到新数组,后者避免了复制数组的开销。
(注明:StringBuffer是字符串缓冲区,是一个容器,用于存储,删除,获取,修改,长度是可变的,可以操作多个数据类型,最终会通过tostring()方法编程字符串,并且是线程同步的,效率会降低)
四.字符串的匹配:
1.模式匹配算法的意义:
现今大多数入侵检测系统还是采用的基于规则的模式匹配策略,模式匹配算法的好坏直接影响到入侵检测系统的准确性和实时性。
(1).定义:假设文本是一个长度为n的数组T[1..n],而模式是一个长度为m的数组p[1…m],其中m<=n,进一步假设p和t的元素都是来自一个有限字母集∑的字符。
注明:1.子串是定位操作通常称为串的模式匹配。子串也称为模式串。
2.有效偏移:如果0<=s<=n-m,并且T[s+1..s+m]=P[1..m](即如果T[s+j]=P[j],其中1<=j<=m),那么称模式P在文本T中出现,且偏移为s(或者等家地,模式P在文本T中出现的位置是以s+1开始的)。如果P在T中以偏移s出现,则称为有效偏移。
(2).字符串匹配算法及其预处理时间:
算法说明:每个算法的总运行时间是预处理时间和匹配时间的和。
(3).朴素的模式匹配算法:
1.基本原理:朴素字符串匹配算法是通过一个循环找到所有有效的偏移,该循环对n-m+1个可能的s值进行检测,看是否满足条件P[1..m]=T[s+1..s+m].
2.具体解析:该算法也称为布鲁特—福斯算法,其基本思想是从主串的第一个字符与模式串的第一个字符比较,若相等,则继续逐对字符进行后续的比较,否则从主串第二个字符起与模式串的第一个字符重新比较,直至模式串中每个字符依次和主串中一个连续的字符序列相等时为止,此时称为匹配成功。如果不能在主串中找到与模式串相同的子串,则匹配失败。
(注解:朴素字符串匹配过程可以形象的堪称一个包含模式的“模版”沿文本滑动,同时对每个偏移都要检测模版上的字符是否与文本中对应的字符相等。)
(1).算法演示:
例如主串s为:ababcabcacbab,模式串t为:abcacb,匹配过程如下:
第1趟:ababcabcacbab i=3
abc j=3 匹配失败
第2趟:ababcabcacbab i=2
a j=1 匹配失败
第3趟:ababcabcacbab i=7
abcac j=5 匹配失败
第4趟:ababcabcacbab i=4
a j=1 匹配失败
第5趟:ababcabcacbab i=5
a j=1 匹配失败
第6趟:ababcabcacbab i=11
abcacb j=6 匹配成功
如上所示,当第6趟匹配的时候在s中找到t,此时匹配成功,返回与t第1个字符相等的字符在s中出现的位置。
(2).代码实现:
//匹配算法:主串s,模式t
int index(char *s, char *t)
{
int i = 0, j = 0;
while(i < strlen(s) && j < strlen(t))
{
if(s[i] == t[j])
{
i++; j++;
}
else //每一趟匹配失败时重新计算主串s和模式串t的索引
{
i = i - j + 1;
j = 0;
}
}
if(j >= strlen(t)) //匹配成功
return i - j + 1;
else //匹配失败
return -1;
}
(3). 算法分析:假设主串和模式串的长度分别为n和m,位置序号从0开始计算。
1. 分析朴素模式匹配算法的时间复杂度:
设从主串的第i个位置开始与模式串匹配成功,在前i趟匹配中(位置0—i-1),每趟不成功的匹配都是模式串的第一个字符与主串中相应的字符不相同,则在前i趟匹配中,字符的比较共进行了i次,而第i+1趟(从位置i开始)成功匹配的字符比较次数为m,所以总的字符比较次数为i+m(0<=i<=n-m)。若主串的n-m个起始位置上匹配成功的概率相同,则在最好的情况下匹配成功时字符间的平均比较次数为:
①最好的情况下,
第i个位置匹配成功,比较了(i-1+m)次,平均比较次数:
最好情况下算法的平均时间复杂度O(n+m)。
②最坏的情况下,
第i个位置匹配成功,比较了(i*m)次,平均比较次数:
设n>>m,最坏情况下的平均时间复杂度为O(n*m)。
2. 改进的模式匹配算法(KMP算法)
(注明:K-M-P算法是线性时间字符匹配算法,次算法无需计算转移函数,匹配时间,只用到辅助函数,它在匹配时间内根据模式预先计算出来,并且存储在数组中)
KMP算法的基本思想是:若某趟匹配过程中Ti和Pj不匹配,而前j一1个字符已经匹配。此时只需右移模式串P,目标串T不动,即指针i不回溯,让Pk与Ti继续比较。移动后重新开始比较的位置k仅与模式串P有关,而与目标串T无关,因此k可以通过下面的next函数事先确定。
定义next[j]函数为:
过程演示:
怎么求串的模式值next
定义:
(1)next[0]= -1 意义:任何串的第一个字符的模式值规定为-1。
(2)next[j]= -1 意义:模式串T中下标为j的字符,如果与首字符
相同,且j的前面的1—k个字符与开头的1—k
个字符不等(或者相等但T[k]==T[j])(1≤k<j)。
如:T=”abCabCad” 则 next[6]=-1,因T[3]=T[6]
(3)next[j]=k 意义:模式串T中下标为j的字符,如果j的前面k个
字符与开头的k个字符相等,且T[j] != T[k] (1≤k<j)。
即T[0]T[1]T[2]。。。T[k-1]==
T[j-k]T[j-k+1]T[j-k+2]…T[j-1]
且T[j] != T[k].(1≤k<j);
(4) next[j]=0 意义:除(1)(2)(3)的其他情况。
代码实现:
void get_nextval(const char* pattern, int next[]){
int j=0, k=-1;
next[0] = -1;
while(pattern[j] != '\0'){
if(k!=-1 && pattern[k]!=pattern[j]){
k = next[k];
}
k++;
j++;
if(pattern[k] == pattern[j]){
next[j] = next[k];
}else{
next[j] = k;
}
}
}
//KMP算法,匹配成功返回匹配位置(首字符索引),失败返回-1
int KMP(const char *text, const char *pattern){
int len = strlen(pattern);
int *next = new int[len+1];
int index=0, i=0, j=0;
if(!text || !pattern || '\0'==text[0] || '\0'==pattern[0]){
cerr<<"空指针或空串,KMP执行失败!"<<endl;
exit(0);
}
get_nextval(pattern, next); //求匹配串的next数组值
while('\0'!=text[i] && '\0'!=pattern[j]){
if(text[i] == pattern[j]){
i++; //继续比较后续字符
j++; //
}else{
index += j-next[j];
if(next[j] != -1)
j = next[j]; //模式串向右移动
else //若next[j]==1,表示pattern[j]与text[i]已经间接匹配过了,不相等。
{
j = 0;
i++;
}
}
}
delete[] next;
if('\0' == pattern[j])
return index; //匹配成功
else
return -1;
}
void main(){
char * text = "bababCabCadcaabcaababcbaaaabaaacababcaabc";
char * pattern = "adcaab";
cout<<KMP(text, pattern)<<endl;
}
一.基本概念:
1.数据类型:
(1).值类型:表示该数据类型存储是一个数据值,基于值类型的变量直接包含指。
a.简单数据类型,b.结构类型,c.枚举类型
(2).值类型:(又名:对象)。
(3).指针类型:指向值类型或者引用类型的数据类型。
(注明:在java中string不是基本数据类型,但在C#中string是基本数据类型)
2.字符串定义:是仅有字符构成的有限序列,是取值范围受限的线性表。
(线性表定义:是由n(n>=0)个元素的有限序列)
3.字符串的存储:
(1).顺序存储:是指用一组地址连续的存储单元来存储船只的字符串序列。
(2).链式存储:当采用链表存储字符串的时候,每一个结点中可以存储一个字符,也可以存储多个字符。
(链式存储:是用结点来存储数据元素,(结点如下))
二.字符串操作:
1.字符串的赋值:字符串的默认值为null。
注明:
(1).null注解:
(a.)本意是“空的;元素只有零的”意思。计算机中通常表示空值,无结果,或是空集合。
(b).NULL指针 :NULL指针是一个无类型指针,并且值为0.
(c).数据库中的NULL:Null在数据库中表示 不知道的数据,主要有3种意思:
1)知道数据存在,但不知道具体值.
2)不知道数据是否存在.
3)数据不存在.
(d).java和c#中的null是一个关键字,通常用来声明一个空引用,Objecto = null,表示o对象是一个空引用,如果引用null,就会抛出一个NullPointerException异常。
(2).对于常量,编译的时候就直接存储他们的子面值,而不是他们的引用,如string s=”a”+”b”+”c”;次字符串就只产生一个对象。在string内部是通过stringbuilder实现的。
(3).string pool:是对应在常量池中存储string常量的区域,相同的string常量在常量池中只创建一个。
(4).string是使用final来实现字符串的存储,在string对象创建之后,就不能再修改次对象中存储的字符串内容。
2.基本操作:
(1)字符访问(下标访问s[i])
(2)打散为字符数组(ToCharArray)
(3)截取子串(Substring)
(4)匹配索引 (indexof())
(5))大小写转换(ToUpper和ToLower)
(6).填充对齐(PadLeft和PadRight)
(7).截头去尾(Trim)
(8).插入和删除(Insert和Remove)
(9).替换字符(串)(Replace)
(10).分割为字符串数组(Split)——互逆操作:联合一个字符串静态方法Join(seperator,arr[])
(11).格式化(静态方法Format)
(12).连接成一个字符串(静态方法Concat、静态方法Join和实例方法StringBuilder.Append)
例1:模拟一个trim()方法,去掉字符串两端的空格?
(1).判断字符串的第一个位置是否为空格,如果是,则继续向下判断,直到不是空格为止,结尾处判断也是如此。
(2).当开始和结尾都判断不是空格时就获取字符串。
例2:将一个字符串进行反转,将字符串中指定的部分进行反转:
(1).回忆数据元素的反转。
(2).将字符串编程数组,对数组反转。
(3).将反转的数组编程字符串。
(4).只要将反转的部分的开始和结束为止作为参数传递即可。
例3:获取一个字符串在另一个字符串中出现的次数:
(1).定义一个计数器。
(2).获取字符第一次出啊先的位置。
(3)从第一次出现位置后剩余的字符串中继续获取字符出现的位置。
(4).当获取不到时,计数完成。
3.字符串的比较:
(1).“==”是比较地址。只要使用“=”运算符,就会调用Equals( )方法,的以Equals( )方法与“=”是等价的。String.Equals方法来优化比较过程该方法能自动地完成引用比较和值比较。
(2).String.Compare是一个比较灵活的比较方法,由于考虑到大小写或文化因素,就可以使用此方法。因为它有许多重载的形式,可以接受大小写或文化的参数,也支持子串比较。
(3).String.CompareOrdinal是对严格基于字符的序数值完成比较。它其实是使用一种简单的算法来比较2个字符串的Unicode值。如果第一个字符串小于第二个字符串,则返回小于0;如果是2个字符串相等,返回0;如果第一个字符串大于第二字符串,则返回大于0的值。
三.String与StringBuffer比较:
(1).string re=”a”+”b”;与StringBuffer re=new string().append(“a”).append(“b”);
前者的效率要高于后者,因为在”a”+”b”处理后,才会将值赋给re,之开辟了一次内存。
(2).public String getString(String s1,String s2){
Return s1+s2;
}
Public String getString(String s1,String s2){
Return new StringBuffer().append(s1).append(s2);
}
两者的效率相同,因为开辟一个内存段,再合并(或者叫扩展)内存
(3).String s=”s1”; s+=”s2”;
StringBuffer s=new StringBuffer().append(“s1”).append(“s2”)
前者的效率高于后者,因为string是不可变的对象,每次“+=”操作都会构造新的string对象。
(4).StringBuffer s=new StringBuffer();
For(int i=0;i<5000;i++){
S.append(“a”);
}
StringBuffer s=new StringBuffer(25000);
For(int i=0;i<5000;i++){
S.append(“a”)
}
前者的效率高于后者,因为StringBuffer内部实现的是char数组,默认初始化长度为16,每当字符串长度大于char数组长度的时候,就会构造更大的新数组,并将原先的数组内容复制到新数组,后者避免了复制数组的开销。
(注明:StringBuffer是字符串缓冲区,是一个容器,用于存储,删除,获取,修改,长度是可变的,可以操作多个数据类型,最终会通过tostring()方法编程字符串,并且是线程同步的,效率会降低)
四.字符串的匹配:
1.模式匹配算法的意义:
现今大多数入侵检测系统还是采用的基于规则的模式匹配策略,模式匹配算法的好坏直接影响到入侵检测系统的准确性和实时性。
(1).定义:假设文本是一个长度为n的数组T[1..n],而模式是一个长度为m的数组p[1…m],其中m<=n,进一步假设p和t的元素都是来自一个有限字母集∑的字符。
注明:1.子串是定位操作通常称为串的模式匹配。子串也称为模式串。
2.有效偏移:如果0<=s<=n-m,并且T[s+1..s+m]=P[1..m](即如果T[s+j]=P[j],其中1<=j<=m),那么称模式P在文本T中出现,且偏移为s(或者等家地,模式P在文本T中出现的位置是以s+1开始的)。如果P在T中以偏移s出现,则称为有效偏移。
(2).字符串匹配算法及其预处理时间:
算法 | 预处理时间 | 匹配时间 |
朴素算法 | 0 | O((n-m+1)m) |
R-K算法 | ⊙(m) | O((n-m+1)m) |
有限自动机算法 | O(m| ∑|) | ⊙(n) |
K-M-P算法 | ⊙(m) | ⊙(n) |
(3).朴素的模式匹配算法:
1.基本原理:朴素字符串匹配算法是通过一个循环找到所有有效的偏移,该循环对n-m+1个可能的s值进行检测,看是否满足条件P[1..m]=T[s+1..s+m].
2.具体解析:该算法也称为布鲁特—福斯算法,其基本思想是从主串的第一个字符与模式串的第一个字符比较,若相等,则继续逐对字符进行后续的比较,否则从主串第二个字符起与模式串的第一个字符重新比较,直至模式串中每个字符依次和主串中一个连续的字符序列相等时为止,此时称为匹配成功。如果不能在主串中找到与模式串相同的子串,则匹配失败。
(注解:朴素字符串匹配过程可以形象的堪称一个包含模式的“模版”沿文本滑动,同时对每个偏移都要检测模版上的字符是否与文本中对应的字符相等。)
(1).算法演示:
例如主串s为:ababcabcacbab,模式串t为:abcacb,匹配过程如下:
第1趟:ababcabcacbab i=3
abc j=3 匹配失败
第2趟:ababcabcacbab i=2
a j=1 匹配失败
第3趟:ababcabcacbab i=7
abcac j=5 匹配失败
第4趟:ababcabcacbab i=4
a j=1 匹配失败
第5趟:ababcabcacbab i=5
a j=1 匹配失败
第6趟:ababcabcacbab i=11
abcacb j=6 匹配成功
如上所示,当第6趟匹配的时候在s中找到t,此时匹配成功,返回与t第1个字符相等的字符在s中出现的位置。
(2).代码实现:
//匹配算法:主串s,模式t
int index(char *s, char *t)
{
int i = 0, j = 0;
while(i < strlen(s) && j < strlen(t))
{
if(s[i] == t[j])
{
i++; j++;
}
else //每一趟匹配失败时重新计算主串s和模式串t的索引
{
i = i - j + 1;
j = 0;
}
}
if(j >= strlen(t)) //匹配成功
return i - j + 1;
else //匹配失败
return -1;
}
(3). 算法分析:假设主串和模式串的长度分别为n和m,位置序号从0开始计算。
1. 分析朴素模式匹配算法的时间复杂度:
设从主串的第i个位置开始与模式串匹配成功,在前i趟匹配中(位置0—i-1),每趟不成功的匹配都是模式串的第一个字符与主串中相应的字符不相同,则在前i趟匹配中,字符的比较共进行了i次,而第i+1趟(从位置i开始)成功匹配的字符比较次数为m,所以总的字符比较次数为i+m(0<=i<=n-m)。若主串的n-m个起始位置上匹配成功的概率相同,则在最好的情况下匹配成功时字符间的平均比较次数为:
①最好的情况下,
第i个位置匹配成功,比较了(i-1+m)次,平均比较次数:
最好情况下算法的平均时间复杂度O(n+m)。
②最坏的情况下,
第i个位置匹配成功,比较了(i*m)次,平均比较次数:
设n>>m,最坏情况下的平均时间复杂度为O(n*m)。
2. 改进的模式匹配算法(KMP算法)
(注明:K-M-P算法是线性时间字符匹配算法,次算法无需计算转移函数,匹配时间,只用到辅助函数,它在匹配时间内根据模式预先计算出来,并且存储在数组中)
KMP算法的基本思想是:若某趟匹配过程中Ti和Pj不匹配,而前j一1个字符已经匹配。此时只需右移模式串P,目标串T不动,即指针i不回溯,让Pk与Ti继续比较。移动后重新开始比较的位置k仅与模式串P有关,而与目标串T无关,因此k可以通过下面的next函数事先确定。
定义next[j]函数为:
过程演示:
怎么求串的模式值next
定义:
(1)next[0]= -1 意义:任何串的第一个字符的模式值规定为-1。
(2)next[j]= -1 意义:模式串T中下标为j的字符,如果与首字符
相同,且j的前面的1—k个字符与开头的1—k
个字符不等(或者相等但T[k]==T[j])(1≤k<j)。
如:T=”abCabCad” 则 next[6]=-1,因T[3]=T[6]
(3)next[j]=k 意义:模式串T中下标为j的字符,如果j的前面k个
字符与开头的k个字符相等,且T[j] != T[k] (1≤k<j)。
即T[0]T[1]T[2]。。。T[k-1]==
T[j-k]T[j-k+1]T[j-k+2]…T[j-1]
且T[j] != T[k].(1≤k<j);
(4) next[j]=0 意义:除(1)(2)(3)的其他情况。
代码实现:
void get_nextval(const char* pattern, int next[]){
int j=0, k=-1;
next[0] = -1;
while(pattern[j] != '\0'){
if(k!=-1 && pattern[k]!=pattern[j]){
k = next[k];
}
k++;
j++;
if(pattern[k] == pattern[j]){
next[j] = next[k];
}else{
next[j] = k;
}
}
}
//KMP算法,匹配成功返回匹配位置(首字符索引),失败返回-1
int KMP(const char *text, const char *pattern){
int len = strlen(pattern);
int *next = new int[len+1];
int index=0, i=0, j=0;
if(!text || !pattern || '\0'==text[0] || '\0'==pattern[0]){
cerr<<"空指针或空串,KMP执行失败!"<<endl;
exit(0);
}
get_nextval(pattern, next); //求匹配串的next数组值
while('\0'!=text[i] && '\0'!=pattern[j]){
if(text[i] == pattern[j]){
i++; //继续比较后续字符
j++; //
}else{
index += j-next[j];
if(next[j] != -1)
j = next[j]; //模式串向右移动
else //若next[j]==1,表示pattern[j]与text[i]已经间接匹配过了,不相等。
{
j = 0;
i++;
}
}
}
delete[] next;
if('\0' == pattern[j])
return index; //匹配成功
else
return -1;
}
void main(){
char * text = "bababCabCadcaabcaababcbaaaabaaacababcaabc";
char * pattern = "adcaab";
cout<<KMP(text, pattern)<<endl;
}
相关文章推荐
- Android之获取手机上的图片和视频缩略图thumbnails
- android string.xml文件中的整型和string型代替
- Android java 与 javascript互访(相互调用)的方法例子
- String.intern
- Tomcat端口被占用解决方法(不用重启)
- 动易2006序列号破解算法公布
- Prototype源码浅析 String部分(二)
- “传奇”图象数据存储方式
- Ruby中的String对象学习笔记
- Ruby实现的矩阵连乘算法
- C#插入法排序算法实例分析
- PostgreSQL ERROR: invalid escape string 解决办法
- C#数据结构与算法揭秘二
- 算法练习之从String.indexOf的模拟实现开始
- C#算法之关于大牛生小牛的问题
- SQL Server误区30日谈 第18天 有关FileStream的存储,垃圾回收以及其它
- C#实现的算24点游戏算法实例分析
- c语言实现的带通配符匹配算法
- 浅析STL中的常用算法
- 算法之排列算法与组合算法详解