阿里巴巴2015年秋季在线笔试附加题---求交集字符串

来源:互联网 发布:webshell密码暴力破解 编辑:程序博客网 时间:2024/05/21 08:53

给定一个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(n²)),对于很长的字符串他的作用才能明显发挥.

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){   intkey=(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)

效率还可以吧..测了几个例子都

0 0
原创粉丝点击