字符串hash 自己的一点见解

题目

有一个口吃的外星人,说的话里包含很多重复的字符串,比如babab包含两个bab。给出这个外星人说的一句话,找出至少出现m次的最长字符串。

输入:

输入包含多组数据。每组数据第一行为整数m,第二行为一个仅包含小写字母的字符串,长度在m~40000之间。输入结束博阿直为m=0。

输出:

对于每组数据,如果不存在,则输出'none',否则输出两个整数,即最长字符串的长度及其起始位置的最大值。

样例输入:

3
baaaababababbababbab
11
baaaababababbababbab
3
cccccc
0

样例输出:


5 12
none
4 2


分析


一、

首先想到的是应该是暴力程序;


枚举串的长度,再进行验证,暴力生成所有该长度的子串匹配看是否在原串中出现过至少n次。


时间复杂度应为O(len^4)?(len 为 原串长度)


枚举长度 len 次运算,生成子串 len次运算 暴力匹配 len^2 次运算。


时间复杂度太高了,,肯定是没有能力通过40000的规模的


所以尝试怎么去优化算法。


二、


由于这是我们正在讲的例题,所以说我直接介绍运用 字符串hash 表 的做法。


和hash表 类似,将字符进行编码转译储存在hash 表中,


二分猜出 子串 长度len,, 建立所有长度为len 的 字符串 的hash 表


如果两个串对应的 hash 值相等,认为两字符串相等


PS:做题时卡了好久,,在计算hash值时,所选的乘方底数应该 尽量去选择一个素数。

这里直接附上我认为解释的比较清楚的网址,留作参考。


点击打开链接

点击打开链接


类似的,我在做题时选择使用 ull 让它自然溢出,

如果是 要 模 一个值  , 也尽量选择一个大质数



总体思路如上,代码附下

#include<cstdio>
#include<cstring>
#include<iostream>
#include<cctype>
#include<cstdlib>
#include<vector>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<queue>
#include<stack>
using namespace std;

typedef long long ll;
typedef unsigned long long ull;

void _scanf(int &x)
{
	char ch;
	int f=0;
	
	ch=getchar();
	
	while(!isdigit(ch))
	{
		if(ch=='-') f=1;
		ch=getchar();
	}
	
	x=0;
	
	while(isdigit(ch))
	{
		x=x*10+ch-'0';
		ch=getchar();
	}
	
	if(f) x=-x;
}

void _scanf(ll &x)
{
	char ch;
	int f=0;
	
	ch=getchar();
	
	while(!isdigit(ch))
	{
		if(ch=='-') f=1;
		ch=getchar();
	}
	
	x=0;
	
	while(isdigit(ch))
	{
		x=x*10+ch-'0';
		ch=getchar();
	}
	
	if(f) x=-x;
}

void _scanf(int &x,int &y)
{
	_scanf(x);
	_scanf(y);
}

void _scanf(int &x,int &y,int &z)
{
	_scanf(x,y);
	_scanf(z);
}

const int maxn=50005;
const int p=7;

struct data
{
	int id;
	ull w;
};

bool cmp(data a,data b)
{
	if(a.w!=b.w) return a.w<b.w;
	
	return a.id<b.id;
}

ull H[maxn];
ull pw[maxn];
data hash[maxn];
char s[maxn];
int m;
int pos;

void ready(char *s,int p)
{
	int n=strlen(s);
	pw[0]=1;
	
	for(int i=1;i<=n;i++) pw[i]=pw[i-1]*p;
	
	H[n]=0;
	for(int i=n-1;i>=0;i--) H[i]=H[i+1]*p+s[i]-'a';
}

void getv(char *s,int L)
{
	int n=strlen(s);
	
	for(int i=0;i+L<=n;i++)
	 hash[i]=(data){i,(H[i]-H[i+L]*pw[L])};
	 
	sort(hash,hash+n-L+1,cmp);
}

int check(int mid)
{
	getv(s,mid);
	int n=strlen(s);
	int cnt=0;
	pos=-1;
	
	for(int i=0;i+mid<=n;i++)
	{
		if(i==0 || hash[i].w!=hash[i-1].w) cnt=0;
		if(++cnt>=m) pos=max(pos,hash[i].id);
	}
	
	return pos>=0;
}

void solve()
{
	while(1)
	{
		_scanf(m);
		if(m==0) break;
		
		scanf("%s",s);
		
		ready(s,p);
		
		if(!check(1))
		{
			printf("none\n");
		}
		else
		{
			int A=1,B=strlen(s)+1,ans=0,ansd=0,mid;
			
			while(A<=B)
			{
				mid=(A+B)/2;
				
				if(check(mid))
				{
					A=mid+1;
					ans=mid;
					ansd=pos;
				}
				else
				{
					B=mid-1;
				}
			}
			
			printf("%d %d\n",ans,ansd);
		}

	}
}

int main()
{
//	freopen("in.txt","r",stdin);
//	freopen("out.txt","w",stdout);
	
	solve();
}


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值