一、 串类型的定义
- 1. 串的定义
串(string)(或字符串)是由零个或多个字符组成的有序序列,一般记为
S=”a1a2…an” (n>=0)
其中,s是串的名,用双引号括起来的字符序列是串的值;ai (1≤i≤n)可以是字母、数字或其他字符;串中字符的数目n成为串的长度。零个字符的串称为空串(null string),它的长度为0。
串中任意个连续的字符组成的子序列称为该串的子串。包含子串的串相应的称为主串。通常称字符在序列中的序号为该字符在串中的位置。子串在主串中的位置则以子串的第一个字符在主串中的位置来表示。
例如,假设有a、b、c、d四个字符串:
a=”BEI” b=”JING”
c=”BEIJING” d=”BEI JING”
则它们的长度分别为3、4、7、8;并且a、b都是c、d的子串,a在c和d中的位置都是0,而b在c中的位置是3,在d中的位置是4;可见字符串的索引是从0开始的。
称两个串相等的,当且仅当这两个串的值相等。也就是说,只有当两个字符串的长度相等,并且各个对应位置的字符都相等才相等。例如上例子中a、b、c、d都互不相等。
值得一提的是,串值必须用一对双引号括起来,但双引号本身不属于串,它的作用只是为了避免与变量名或数的常量混淆而已。另在高级语言中,如C语言中字符串中隐藏一个特殊的字符即’\0’,它的作用是标识字符串结束,因此称’\0’为字符串结束符。
- 2. 串的操作
字符串可以有丰富的操作,在日常非数值运算大量的都是字符串的操作。归纳总结可以大致分为以下操作
(1)赋值 strcpy(数组名、字符串);
(2)判空 strlen() strcmp()
(3)字符串比较 strcmp
(4)求字符串长度 strlen
(5)字符串拷贝 strcpy
(6)字符串连接 strcat
(7)求字符串子串(截取字符串)
二、 串类型的表示和实现
- 1. 串的表示
(1)定长顺序存储表示:类似线性表的顺序存储结构,用一组地址连续的存储单元存储字符串的字符序列。
(2)堆分配存储表示:这种存储方式特点是:仍一一组连续的存储单元存放字符串序列,但它们的存储空间是在程序执行过程中动态分配而得的。
(3)块链存储表示:和线性表的链式存储类似,也是用链表来保存字符串的值。根据串的特殊,若每个字符占一个结点太小,则采取每个结点可以存放一个也可以存放多个字符。如图所示:图a就是块链存储方式,而图b则过于浪费空间。
- 1. 串的实现
首先串的存储方式不同其实现方式就是不一样的,这里我们采用第一种存储方式即定长顺序存储表示。
#define MAX 255 //用户可在255以内定义最大串长
typedef unsigned char SString[MAX+1]; //最后以’\0’结束标识字符串结束
当如此顶以后串的各种操作就可以实现了,有一些C语言中自带的系统可完成一部分,如字符串拷贝strcpy,字符串连接strcat,字符串比较strcmp,求字符串长度strlen。也有另外一部分需要自己实现,比如截取字符串substring:
char * substring(char sub[],char s[],int pos,int len) { int i,count; if(pos<0 ||pos>strlen(s)-1||len<0||len>strlen(s)-pos) //判断pos(开始截取位置)是否合法 { //判断len(截取长度)是否合法 printf("输入参数有误"); return "error"; } else { count=0; for(i=pos;i<pos+len;i++) //截取字符串 { sub[count]=s[i]; count++; } return sub; } } void main() { char s[100]="I love BAWEI University"; char sub[20]; printf("%s",substring(sub,s,2,4)); }
运行效果如图所示:
一、 串的模式匹配算法
子串的定位操作通常称为串的模式匹配,是各种串处理系统中最重要的操作之一。
- 1. ACM算法:子串的定位函数Index(S,T,pos)
int index(char s[],char t[],int pos)//返回子串t在主串s中的位置,pos指定的是在主串的第pos开始出现子串的位置,若全串查找pos设置为0 { int i,j; i=pos; j=0; while(i<strlen(s)&&j<strlen(t)) { if(s[i]==t[j]) { i++; j++; //继续比较后面的字符 } else { i=i-j+1; //i后退重新开始匹配 j=0; } } if(j==strlen(t)) { return i-strlen(t); } else { return -1; //没有匹配到 } } void main() { char s[100]="ababcabcacbaab"; char t[100]="abcac"; int pos=0; printf("%d",index(s,t,pos)); }
运行结果是5,即在主串下标5的位置出现abcac这个子串。下面我们看下这个匹配的过程示意图:
↓i=2
第一趟匹配 a b a b c a b c a c b a b
a b c
↑j=2
↓i=1
第二趟匹配:a b a b c a b c a c b a b
a
↑j=0
↓i=6
第三趟匹配:ab a b c a b c a c b a b
a b c a c
↑j=4
↓i=3
第四趟匹配:a b a b c a b c a c b a b
a
↑j=0
↓i=4
第五趟匹配:a b a b c a b c a c b a b
a
↑j=0
↓i=10
第六趟匹配:a b a b c a b c a c b a b
a b c a c
↑j=5
- 2. ACM算法:模式匹配的一种改进算法(KMP算法)
这种改进算法是D.E.Knuth与V.R.Pratt和J.H.Morris同时发现的,因此人们称它为克努特-莫里斯-普拉特操作(简称KMP算法)。此算法可以在O(n+m)的时间数量级上完成串的模式匹配操作。其改进在于:每趟匹配过程出现字符比较不等时,不需要回溯i指针,而是利用已经得到的“部分匹配”的结果将模式串向右“滑动”尽可能远的一段距离后,继续进行比较。下面先从具体例子看起。
回顾上小节的匹配过程示例,在第三趟的匹配过程中,当i=6,j=4字符比较不等时,又从i=3,j=0重新开始比较。然后,经仔细观察可发现,在i=3和j=0,i=4和j=0,及i=5和j=0这三次比较都是不必进行的。因为从第三趟部分匹配的结果就可得出,主串中下标3、4、5的字符必然是‘b’、‘c’、‘a’(即模式串下标为1、2、3的字符)。因为模式中第一个字符是a。因此它无需再和这三个字符进行比较,而仅需将模式向右滑动3个字符的位置继续进行i=6,j=1时的字符比较即可。同理,在第一趟匹配中出现字符不等时,仅需将模式向右移动两个字符的位置继续进行i=2,j=0时的字符比较。由此,在整个匹配过程中,i指针没有回溯,如下图所示。
↓i=2
第一趟匹配 a b a b c a b c a c b a b
a b c
↑j=2
↓i → ↓i=6
第二趟匹配 a b a b c a b c a c b a b
a b c a c
↑ → ↑j=4
↓i → ↓i=10
第三趟匹配 a b a b c a b c a c b a b
(a ) b c a c
↑j=2→↑j=5
现在讨论一般情况。假设主串为“s1s2。。。sn”,模式串为“p1p2。。。pm”,从上例的分析可知,为了实现改进算法,需要解决下述问题:当匹配过程中产生“失配”即(si ≠pj)时,模式串“向右滑动”可行的距离多远,换句话说,当主串下标i的字符与模式中下标j的字符失配(即比较不等)时,主串下标i的字符(i指针不回溯)应与模式中哪个字符再比较。
假设此时应与模式中下标k(k<j)的字符进行比较,则模式中前k-1字符的子串必须满足下列关系式,且不可能存在k’>k满足下列关系式
“p1p2。。。Pk-1”=“si-k+1si-k+2。。。si-1” ①
而已经得到的“部分匹配”的结果是
“pj-k+1pj-k+2。。。Pj-1”=“si-k+1si-k+2。。。si-1” ②
由式子①和式子②可知
“p1p2。。。Pk-1”=“pj-k+1pj-k+2。。。Pj-1” ③
反之,若模式串存在满足式③的两个子串,则当匹配过程中,主串中第i个字符与模式中第j个字符比较不等时,仅需要将模式串向右滑动值模式中第k个字符和主串第i个字符对齐,此时,模式中头k-1个字符的子串“p1p2。。。Pk-1”必定与主串中第i个字符之前长度k-1的子串“si-k+1si-k+2。。。si-1”相等,因此,匹配仅需从模式中第k个字符与主串的第i个字符比较起继续进行。
设next[j]=k,则next[j]表明当第j个字符与主串中相应字符失配时,在模式串需中心和主串中字符进行比较的字符位置。由此可引出模式串的next函数的定义
由此定义可推出下列模式串的next函数值:
算法如下:
int get_next(char t[],int next[]) //计算next[j]的函数 { int i=0,j=0; next[0]=0; while(i<strlen(t)) { if(j==0||t[i]==t[j]) { i++; j++; next[i]=j; } else { j=next[j]; } } } int index(char s[],char t[],int pos) //子串定位函数 { int i,j; int next[256]; get_next(t,next); for(j=0;j<strlen(t);j++) { printf("%d\t",next[j]); } i=pos; j=0; while(i<strlen(s)&&j<strlen(t)) { if(j==-1||s[i]==t[j]) { i++; j++; } else { j=next[j]-1; //失配时只滑动模式串到next[j]-1的位置继续比较即可 } } if(j>=strlen(t)) { return i-strlen(t); } else { return -1; //没有匹配到 } } void main() { char s[100]="ababcabcacbaab"; char t[100]="abcac"; int pos=0; printf("%d",index(s,t,pos)); //打印子串在主串的位置 }
一、 串的算法时间复杂度的分析
一般的来讲,对于字符串模式匹配算法时间复杂度是与主串的长度n和子串的长度m相关的,对于未改进的定位子串的算法:最好情况时间复杂度为O(n+m)而最坏情况则可达到O(n*m),因为其i指针是要回溯的。而对于改进的模式匹配算法KMP算法其i指针不用回溯,只移动模式串的比较位置即可。这样时间复杂度就在O(n+m)级别可完成算法。
生日快乐代码
#include "stdio.h" #include "stdlib.h" #include "windows.h" #include "mmsystem.h" #include "time.h" #pragma comment(lib,"Winmm.lib") int i,j; void gotoxy(int x,int y){ COORD c; c.X=x; c.Y=y; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),c); } int color(int c){ SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),c); return 0; } void xin(){ color(12); gotoxy(18,8); printf("■");//8行28列 Sleep(500); gotoxy(16,7); printf("■"); Sleep(500); gotoxy(14,6); printf("■"); Sleep(500); gotoxy(12,6); printf("■"); Sleep(500); gotoxy(10,7); printf("■"); Sleep(500); gotoxy(10,8); printf("■"); Sleep(500); gotoxy(10,9); printf("■"); Sleep(500); gotoxy(12,10); printf("■"); Sleep(500); gotoxy(14,11); printf("■"); Sleep(500); gotoxy(16,12); printf("■"); Sleep(500); gotoxy(18,13); printf("■"); Sleep(500); gotoxy(20,12); printf("■"); Sleep(500); gotoxy(22,11); printf("■"); Sleep(500); gotoxy(24,10); printf("■"); Sleep(500); gotoxy(26,9); printf("■"); Sleep(500); gotoxy(26,8); printf("■"); Sleep(500); gotoxy(26,7); printf("■"); Sleep(500); gotoxy(24,6); printf("■"); Sleep(500); gotoxy(22,6); printf("■"); Sleep(500); gotoxy(20,7); printf("■"); for(i=7;i<10;i++){ gotoxy(12,i); printf("■"); } for(i=7;i<11;i++){ gotoxy(14,i); printf("■"); } for(i=8;i<12;i++){ gotoxy(16,i); printf("■"); } for(i=9;i<13;i++){ gotoxy(18,i); printf("■"); } for(i=7;i<10;i++){ gotoxy(24,i); printf("■"); } for(i=7;i<11;i++){ gotoxy(22,i); printf("■"); } for(i=8;i<12;i++){ gotoxy(20,i); printf("■"); } } void xinr(){ color(4); for(i=10;i<50;i+=2){ gotoxy(i,15); if(i%2==0){ printf("◢");} else{ printf("◣"); } Sleep(100); } color(12); for(i=8;i<52;i+=2){ gotoxy(i,16); printf("■"); Sleep(100); } color(4);//11浅蓝色 for(i=6;i<54;i+=2){ gotoxy(i,17); printf("■"); Sleep(100); } color(15); for(i=6;i<54;i+=2){ gotoxy(i,18); printf("■"); Sleep(100); } color(5);//紫色 for(i=6;i<54;i+=2){ gotoxy(i,19); printf("■"); Sleep(100); } color(12); for(i=4;i<56;i+=2){ gotoxy(i,20); printf("■"); Sleep(100); } //蜡烛 color(6); gotoxy(14,11); printf("▲"); Sleep(100); color(12); for(i=15;i>12;i--){ gotoxy(14,i); printf("■"); Sleep(100); } for(i=15;i>12;i--){ gotoxy(16,i); printf("■"); Sleep(100); } color(6); gotoxy(16,11); printf("▲"); Sleep(100); color(6); gotoxy(26,11); printf("▲"); Sleep(100); color(12); for(i=15;i>12;i--){ gotoxy(20,i); printf("■"); Sleep(100); } for(i=15;i>11;i--){ gotoxy(26,i); printf("■"); Sleep(100); } color(6); gotoxy(20,11); printf("▲"); Sleep(100); color(6); gotoxy(32,11); printf("▲"); Sleep(100); color(12); for(i=15;i>11;i--){ gotoxy(32,i); printf("■"); Sleep(100); } color(6); gotoxy(44,11); printf("▲"); Sleep(100); color(12); for(i=15;i>12;i--){ gotoxy(44,i); printf("■"); Sleep(100); } color(6); gotoxy(38,11); printf("▲"); Sleep(100); color(12); for(i=15;i>12;i--){ gotoxy(38,i); printf("■"); Sleep(100); } for(i=15;i>12;i--){ gotoxy(46,i); printf("■"); Sleep(100); } color(6); gotoxy(46,11); printf("▲"); Sleep(100); } void menu(){ for(i=0;i<58;i+=2){ gotoxy(i,1); color(7);//4红色5紫色7白色9蓝色10lvse printf("□"); gotoxy(i,26); printf("□"); } for(i=0;i<26;i++){ gotoxy(0,i); printf("□"); gotoxy(56,i); printf("□"); } for(i=2;i<56;i+=2){ for(j=2;j<26;j++){ gotoxy(i,j); color(0); printf("■"); } } } //happy birthday void birthday(){ int i; color(6); //56 20 H for(i=22;i<25;i++){ gotoxy(10,i); printf("■"); } gotoxy(12,23); printf("■"); gotoxy(14,22); printf("◣"); for(i=23;i<25;i++){ gotoxy(14,i); printf("■"); } Sleep(400); //a gotoxy(18,24); printf("■"); gotoxy(18,23); printf("◢"); gotoxy(20,22); printf("▲"); gotoxy(20,23); printf("▁"); gotoxy(22,24); printf("■"); gotoxy(22,23); printf("◣"); //p左边是列 Sleep(400); color(2); gotoxy(26,22); printf("◤"); gotoxy(26,23); printf("◣"); gotoxy(26,24); printf("■"); gotoxy(28,22); printf("◥"); gotoxy(28,23); printf("◢"); //p Sleep(400); color(5); gotoxy(32,22); printf("◤"); gotoxy(32,23); printf("◣"); gotoxy(32,24); printf("■"); gotoxy(34,22); printf("◥"); gotoxy(34,23); printf("◢"); //y Sleep(400); color(4); gotoxy(38,22); printf("◢"); gotoxy(38,23); printf("■"); gotoxy(40,25); printf("■"); gotoxy(40,23); printf("■"); for(i=22;i<25;i++){ gotoxy(42,i); printf("■"); } gotoxy(42,25); printf("◤"); Sleep(400); //b color(10); gotoxy(2,3); printf("◢"); for(i=4;i<7;i++){ gotoxy(2,i); printf("■"); } gotoxy(4,6); printf("◢"); gotoxy(4,5); printf("◥"); //i Sleep(400); color(4); gotoxy(8,3); printf("■"); gotoxy(8,5); printf("■"); gotoxy(8,6); printf("■"); //r Sleep(400); color(12); for(i=4;i<7;i++){ gotoxy(12,i); printf("■"); } //gotoxy(14,5); //printf("■"); gotoxy(14,4); printf("◤"); //t Sleep(400); color(6); gotoxy(18,5); printf("■"); gotoxy(20,5); printf("■"); gotoxy(22,5); printf("■"); gotoxy(20,4); printf("■"); gotoxy(20,6); printf("■"); gotoxy(20,7); printf("■"); gotoxy(22,7); printf("◣"); Sleep(400); //H color(6); for(i=4;i<7;i++){ gotoxy(26,i); printf("■"); } gotoxy(28,5); printf("■"); gotoxy(30,5); printf("◣"); gotoxy(30,6); printf("■"); Sleep(400); //d color(10); for(i=3;i<7;i++){ gotoxy(36,i); printf("■"); } gotoxy(34,6); printf("■"); gotoxy(34,5); printf("◤"); //a Sleep(400); color(12); gotoxy(42,4); printf("■"); gotoxy(40,6); printf("◥"); gotoxy(40,4); printf("◢"); gotoxy(40,5); printf("■"); gotoxy(42,5); printf("■"); gotoxy(42,6); printf("■"); gotoxy(44,6); printf("◤"); //y Sleep(400); color(12); gotoxy(48,3); printf("◢"); gotoxy(48,4); printf("■"); gotoxy(50,4); printf("■"); gotoxy(52,4); printf("■"); gotoxy(52,5); printf("■"); gotoxy(52,6); printf("◤"); gotoxy(52,3); printf("■"); gotoxy(50,6); printf("■"); } void main() { PlaySound(TEXT("sounds\\taiy.wav"),NULL,SND_FILENAME|SND_ASYNC|SND_LOOP); system ( "mode con cols=60 lines=28" ); system("title "); menu(); gotoxy(28,0); system("pause"); xinr(); birthday(); gotoxy(10,0); system("pause"); system("cls"); xin(); }