给定一个query和一个text,均由小写字母组成。要求在text中找出以同样的顺序连续出现在query中的最长连续字母序列的长度。例如, query为“acbac”,text为“acaccbabb”,那么text中的“cba”为最长的连续出现在query中的字母序列,因此,返回结果应该为其长度3。请注意程序效率。
想了好多种方法,姑且先说几种有创意的..
第一种,打卡遮盖型
画得有点丑,有点不规范不要在意,能理解就最好.
明显这是一种空间换时间的算法.
时间复杂度就是O(n+m)
空间复杂度O(n*m)
第二种,过火车匹配
从头过到尾
其实这种就是最普通的一种
时间复杂度O(n*m)
空间复杂度O(1)
第三种,求出next[ ]改进.
正如KMP算法那样,有很多比较是多余可跳过的.
比如说上面的,比较第一个字符a,而后失配时可以直接跳到第4个字符a,再跳到第10个字符a
中间的就可以忽略了,反正都肯定是不匹配的.
不过建立next[ ]也需要一定的时间(O(n2)),对于很长的字符串他的作用才能明显发挥.
void GetNextVal(char *str) { int next [ 30 ]= { 0 }; int len=strlen(str); for( int i = 0 ; i< len ; i++) { if( next [ i ] == 0 ) { int j=i+1; while (str [ i ] != str [ j ] && j < len || next [ j ] != 0 ) j++; if( j != len) next[ i ] = j; else next[ i ] = -1; } } }
第四种,用哈希函数建立索引
这种又是上一种的改进.用哈希建立索引只需要O(n)即可.
比如说:
文本串: 157369824704
对于每个字符进行分类,设哈希函数 f(n)=n mod 10 = d;
那么
f(A[0])=[1] mod 10 =1 ;
f(A[1])=[5] mod 10 =5 ;
f(A[2])=[7] mod 10 =7;
f(A[3])=[3] mod 10 =3 ;
f(A[4])=[6] mod 10 =6;.....
这样可以得到一个索引
索引0: 10->null;
索引1: 0->null;
索引2: 7->null;
索引3: 3->null;
索引4: 8 ->11->null;
索引5: 1->null;
索引6: 4->null;
索引7: 2->9->null;
................
然后假设模式串是59826
对 第一个字符使用哈希函数
得到 对应位置是 先比较位置1,得到最长匹配为1
对第二个使用哈希,得到比较位置5,比较成功得到最长匹配为3
对第三个使用哈希.得到比较位置6,得到最长匹配为2
...............
可见这种拥有是时间复杂度O(n)~O(n*m)是效率不错的.
至于空间复杂度,看哈希函数怎么设定了.
以下是我写的代码,对以上几种想法都采取一点而改进成.
主体还是以一定的空间换时间,而后匹配方面采用Sunday算法加速.
用链表的形式来做索引,用数组来表达得到的key值
这样组合起来就得到一个...(后来查了下) 散列表,也叫hash表. (我想了大半个下午原来已经早被发明也还是教科书的东西∑(?Д?)
先是散列表的数据结构 代码
#include <stdio.h> #include <stdlib.h> //malloc需要用 #include <string.h> #define Len 11 /****************************** 哈希表头 ******************************/ typedef struct HashHeader { struct Node *next; }HashHeader,*PHashHeader; //写了typedef这两个是结构体,不写则是变量 PHashHeader HashTable[Len]; //初始化表头 PHashHeader InitHashTable(void) { PHashHeader node; node=(PHashHeader)malloc (sizeof(PHashHeader)); node->next=NULL; return node; } //初始化哈希表 void InitTable() { for(int i=0;i<Len;i++) HashTable[i]=InitHashTable(); } /******************************* 节点 ******************************/ typedef struct Node { int data; Node *next; }Node,*PNode; PNode PEnd[Len]; //哈希函数 int HashFunc(char c) { int key=(int)c % Len; return key; } //加节点 void AddNode(char c,int i) { PNode node; int key=HashFunc(c); node=(PNode)malloc (sizeof(PNode)); node->data=i; node->next=NULL; if(HashTable[key]->next==NULL) { HashTable[key]->next=node; PEnd[key]=node; } else if(PEnd[key]->next==NULL) { PEnd[key]->next=node; PEnd[key]=node; } else printf("error"); } void PrintHash() { PNode Pr; for(int i=0;i<Len;i++) if(HashTable[i]->next!=NULL ) { Pr=HashTable[i]->next; while(Pr !=NULL) { printf("%4d",Pr->data); Pr=Pr->next; } printf("\n"); } } int main() { char Tstr[30]; printf("输入文本串:"); gets(Tstr); int Tlen=strlen(Tstr); InitTable(); for(int i=0;i<Tlen;i++) AddNode(Tstr[i],i); PrintHash(); return 0; }
以下则是题目要求的函数
int jiaoji(char *Tstr,char *Mstr) { int max=0; int Tlen=strlen(Tstr); int Mlen=strlen(Mstr); InitTable(); int i,j; for(i=0;i<Tlen;i++) AddNode(Tstr[i],i); for(i=0;i<Mlen;i++) { j=HashFunc(Mstr[i]); if(HashTable[j]->next !=NULL ) { PNode Pr=HashTable[j]->next; while(Pr != NULL) { int data=Pr->data; int k=max-1; //类似Sunday算法,从尾部开始比较,反正目的只是取最大 while(Tstr[data+k] == Mstr[i+k] && data+k < Tlen && i+k< Mlen)k++; max=max<k?k:max; Pr=Pr->next; } } } return max; } int main() { char Tstr[30],Mstr[20]; printf("输入较长的串:"); gets(Tstr); printf("输入较短的串:"); gets(Mstr); int max=jiaoji(Tstr,Mstr); printf("最多相交部分长度是%d",max); return 0; }
空间复杂度..O(n+m)
时间复杂度 O(n+m)
效率还可以吧..测了几个例子都
原文地址:http://blog.csdn.net/skyjie6/article/details/39141909