Fancy Signal Translate 【二分 + 滚动哈希】

Fancy Signal Translate 【二分 + 滚动哈希】

链接:https://ac.nowcoder.com/acm/problem/14270
来源:牛客网

题目描述

FST是一名可怜的小朋友,他很强,但是经常fst,所以rating一直低迷。
但是重点在于,他真的很强!他发明了一种奇特的加密方式,这种加密方式只有OIer才能破解。
这种加密方式是这样的:对于一个01串,他会构造另一个01串,使得原串是在新串中没有出现过的最短的串。
现在FST已经加密好了一个串,但是他的加密方式有些BUG,导致没出现过的最短的串不止一个,他感觉非常懊恼,所以他希望计算出没出现过的最短的串的长度。

输入描述:

一行,一个01串。长度≤105

输出描述:

一行,一个正整数,表示没有出现过的最短串的长度。

示例1

输入

100010110011101

输出

4

不清楚题意的我,闷着头就想敲 二分+哈希,都不知道题意让干啥,一直w,后来看了别人代码,才了解到了题意啊啊啊!!!

题意:

​ 给一个 01 组成的字符串,问 最短的 不和字符串的任意子串重复的 串的长度是多少。

例子:

​ 就比如:对于给出的串 11101,那么长度为2的串 00 就没有和11101的任意子串重复。而 1 和 0 这种长度为1的串就在11101 的子串出现过。

思路:

因为我实在太想复习二分和滚动哈希了,就进坑了

  1. 对于答案的情况我们用二分列举出来 ,
  2. 对于判断 mid 是否是答案 ,我们用滚动哈希,得到所有的长度为 mid 的子串的哈希值的个数,用map标记。
  3. 对于所有不重复的标记的数量若小于 1<< mid ,则说明还有某些情况的子串没有出现,则 mid 为可行解。

问题:为什么二进制左移可以判断是否是可行解?

看一个例子,mid = 3, 1<<3 = 8 , 得出来的 8 也就是三位 01 的串的8个种类 :000,001,010,011,100,101,110,111

所以如果遍历出来长度等于 mid 的 01 串的种类数小于 1<<mid 个的话,说明还有一些没有出现过的01串,则mid为可行解,我们用二分找出最小的mid即可。

撸代码:

/*二分 + 滚动哈希*/
#include<string.h>
#include<stdio.h>
#include<map> 
using namespace std;
typedef unsigned long long ull;
ull B = 233;//基数
int n;
char s[100010];
int work(int len){
    
    
	ull val=0,p=1; 
	for(int i=0;i<len;i++){
    
    
		val=val*B+s[i]-'0';
		p*=B;
	}//先得出 0~len-1 的哈希值  val
  	ull temp=val;
  	map<ull,bool>book;
  	book.clear();
  	book[temp]=1;// 标记所出现过的串的哈希值
  	int cnt=1;
	for(int i=0;i+len<n;i++){
    
    
		temp=temp*B+(s[i+len]-'0')-(s[i]-'0')*p;/*滚动哈希*/ 
		if(!book[temp]){
    
    
			cnt++;//记录出现过几种
			book[temp]++;
		}
	}
	return cnt==(1ll<<len);// 判断长为len的 01 串种类是否完全出现过 1LL 转型long long
}
int main()
{
    
    
	scanf("%s",s);
	int r=18,l=1,mid;
	 n=strlen(s);
	/*二分求解*/
	int ans; 
	while(l<=r){
    
    
		mid=(l+r)/2;
		/*如果长度为mid出现重复串*/
		if(work(mid)){
    
    
			l=mid+1;
		}else {
    
    
			r=mid-1;
			ans=mid;
		}
	}
	printf("%d\n",ans);
	return 0;
}

第二种思路:

使用 set 容器直接存储 string 的 长度为 i 的子串

#include<set>
#include<iostream>
using namespace std;
int main()
{
    
    
	string s;
	cin>>s;
	for(int i=1;;i++){
    
    
		
		set<string>book;/*定义set容器  把字符串直接不重复插入*/
		for(int j=0;j+i<=s.size();j++){
    
    
			book.insert(s.substr(j,i));/*从 j 开始 长度为i的子串*/ 
		}
		if(book.size()<(1<<i)){
    
    /*若是出现不重复*/ 
			cout<<i<<endl;
			break;
		}
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/DREAM_yao/article/details/108935957