具体代码就不放出来了,毕竟是一个连数据结构课都还没学的大一萌新,只是在这里分享一下我的思路,一个初学者的小想法。
步骤:
1.读取n,循环读取字符串
2.把每个读到的字符串用哈希求出一个具体的数值(具体怎么求之后说),并且按顺序存入到一个名字叫dictionary的数组之中。
3.对dictionary排序(升降无所谓)
4.读取m,循环读取字符串
5.把每个读取到的字符串用哈希算出一个值
6.在我们已经排好序的dictionary的数组里面找到他的下标(即编号)
7.进行入队等一系列操作(之后再说,重点)通过这个步骤,我们就能把题目的答案求出来了,需要用到两个队列,一个用来存当前字符串对应数值在原dictionary里面的下标,一个储存当我们在第4步读取m后,用这样一个for循环读取字符串时:
for(int i=1;i<=m;i++)
{
scanf("%s",str);
}
这里面的i,我们也要入队,目的就是为了计算区间长度
8.把结果输出即可
让我细说:
好,一个一个解决问题:
1.哈希值如何算?
我们用到字符串哈希里面的一个常用方法:
叫bkdrhash。有兴趣的自己了解一下,我也是没怎么了解就直接用了吼吼
那么具体如何操作呢:
简单,就是对于一个字符串,循环他的每一个字符,由于每一个字符都有对应的ASCII码值,所以我们做这样一个计算:
unsigned int bkdrhash()
{
unsigned int sum=0;
for(int i=1;i<strlen(str);i++)
{
sum=sum*31+str[i];
}
return sum;
}
我知道你可能有很多问号?
为什么要乘以31?
为什么要用unsigned int
如果发生冲突咋办?
好吧,我们一个一个解决:
问题一和问题二属于原理问题,涉及到数学知识,我还是那句话,可以自己找资料学
问题三:
这道题数据比较水,或者说我们的这个bkdrhash冲突几率很小,反正能过这道题。
哈哈
感觉说了跟没说一样啊。
2.为什么要排序
由于每个字符串都能用哈希算出来一个特定大小的数值
所以我们排序之后,再后来的循环之后就可以用二分查找提升效率了
3.细讲第七步
好的,容我细琐:
首先需要用到的:
1.两个队列:
一个为编号队列,一个是位置队列
编号队列用来解决你是谁的问题
位置队列用来解决你在哪里的问题
2.cntline数组,初始化都是0;
下标填入字符串在dictionary数组里面对应的编号
返回值就是他目前在队列中的个数
解析原理:
遇到一个字符串,我们都能用哈希算出其值,
而我们总可以在dictionary数组中找到这个值
并且,都能找到一个独一无二的下标
此时,我们将得到的这个下标(比如a)入队(编号队列)
cntline[a]++;
如果cntline[a]=1,那么说明队列之前都没有这个元素,他是新来的
cntnum++(记录不同的数字个数)
把cntnum和maxnum比较,
如果cntnum大于等于maxnum,则更新maxnum
你问我为什么等于也要更新,别着急,除了求最多,我们还要求最短
此时再比较队列尾部元素对应原下标end和首元素first,长度就是length=end-first+1;
然后让这个length和minlength比较,
如果小于minlength,那么就更新最短值
不小于那就什么都不干
如果cntnum小于maxnum,那么什么都不干(如果按照我这个思路,是不会小于的)
如果cntline[b[i]]大于1,那么说明此时数组中有重复的数字,我们需要保证把不需要的重复数组剔除
如果重复数字在队列中,那么我们就不管,因为它被包含在里面了
此时判断队首元素,line[head]对应的cntline[line[head]]是否大于1
不大于1,不管
大于1
一直出队直到首元素不大于1为止
最终把整个数组2都遍历结束之后,我们就可以把minlength和maxnum输出就可以得到想要的答案了
小细节
1.如果没有出现最大值和最小值,我们不可以直接输出maxnum和minlength,用一个flag,如果maxnum和minlen更新我们让flag=1;最后输出时候看一下就好了
2.此道题不存在大小写敏感
算了,感觉我自己说不清楚,还是放代码把!
代码:
#include<stdio.h>
#include<string.h>
typedef unsigned int ui;
int linew[100005]={0},linep[100005]={0},cntline[1500]={0};
int minlen=20000,maxnum=0,head=1,tail=0,cntnum=0,n,m,flag=0;
char str[15];
ui diction[1500]={0};
ui bkdrhash()
{
ui sum=0;
for(int i=1;i<strlen(str);i++)
{
sum=sum*31+str[i];
}
return sum;
}
int find()
{
ui val=bkdrhash();
int left=1,right=n,mid;
while(1)
{
if(left>right)
{
return -1;
}
mid=(left+right)/2;
if(diction[mid]==val)
{
return mid;
}
else if(diction[mid]>val)
{
right=mid-1;
}
else if(diction[mid]<val)
{
left=mid+1;
}
}
}
void sort()
{
ui tmp;
for(int gap=n/2;gap>0;gap=gap/2)
{
for(int i=gap+1;i<=n;i++)
{
for(int j=i;j-gap>0&&diction[j-gap]>diction[j];j=j-gap)
{
tmp=diction[j-gap];
diction[j-gap]=diction[j];
diction[j]=tmp;
}
}
}
}
void cal(int gps)//核心函数,每次读取strlen的时候更新队列
{
int no=find();
if(no==-1)
{
return ;
}
tail++;
linew[tail]=no;
cntline[no]++;
linep[tail]=gps;
if(cntline[no]==1)
{
cntnum++;
if(cntnum>maxnum)
{
flag=1;
maxnum=cntnum;
minlen=linep[tail]-linep[head]+1;
}
else if(cntnum==maxnum)
{
flag=1;
if(linep[tail]-linep[head]+1<minlen)
{
minlen=linep[tail]-linep[head]+1;
}
}
}
else//如果出队,要随时更新minlen
{
while(1)
{
if(cntline[linew[head]]>1)//出队
{
cntline[linew[head]]--;
head++;//此时判断最小值
if(linep[tail]-linep[head]+1<minlen)
{
flag=1;
minlen=linep[tail]-linep[head]+1;
}
}
else
{
break;
}
}
}
}
void scan()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%s",str);
diction[i]=bkdrhash();
}
}
int main()
{
scan();//扫描+建表
sort();//排序
scanf("%d",&m);
for(int i=1;i<=m;i++)
{
scanf("%s",str);
cal(i);
}
if(flag)
{
printf("%d\n%d\n",maxnum,minlen);
}
else
{
printf("0\n0\n");
}
return 0;
}