牛客 NC15253 白兔的字符串

NC 15253 白兔的字符串

思路:

        可以对t字符串进行一次操作:将第一个字符放到最后面,那么假设操作若干次后得到一个新的字符串t,若存在一个字符串a,若新的t与字符串a的一共子串可以匹配的化,那么称t和a循环同构。 而本题就是求t字符串与目标字符串有多少个循环同构

        首先:可以将t字符串的第一个字符放到最后面,那么就相当于一共环,那么就可以将这个环进行破环成链的操作,也就是开一个原t字符串里两倍的数组,存储破环成链后的操作

        求一个字符串与另一个字符串是否匹配,那么可以用哈希函数进行解决

        先预处理t字符串的哈希函数,然后不断枚举t数组,将每一个长度为原t字符串的长度存储下来

        

//预处理t
void init(int len)
{
	p[0] = 1,h[0] = 1;
	
	for(int i = 1;i<=2 * len;i++)
	{
		p[i] = p[i-  1] * P;
		h[i] = h[i - 1] * P + str[i];
	}
}

//存储每个T字符串长度的子串的哈希值

ULL get(int l,int r)
{
	return h[r] - h[l - 1] * p[r - l + 1];
}


for(int i = 1;i<=len;i++)
	{
		ha.push_back(get(i,i + len - 1));
	}

之后还要对存储的每个T字符串长度的子串的哈希值的数组进行排序,因为要对其进行二分查找答案

其次,在处理目标字符串,过程同上:

//预处理目标字符串的哈希函数
void init1(int len)
{
	p[0] = 1,h1[0] = 1;
	
	for(int i = 1;i<= len;i++)
	{
		
		h1[i] = h1[i - 1] * P + s[i];
	}
}

这里不用存储长度为len的子串的哈希值,因为可以直接通过一个函数得到与循环同构的子串长度相同的子串

ULL get1(int l,int r)
{
	return h1[r] - h1[l - 1] * p[r - l + 1];
}

最后把每个T字符串长度的子串的哈希值,通过二分查找,看存储每个T字符串长度的子串的哈希值的数组中是否存在,存在cnt++;(查找的是在目标字符串中的T字符串长度的子串)



bool check(int l,int r)
{
	ULL x = get1(l,r);
	ULL idx = lower_bound(ha.begin() , ha.end(),x) - ha.begin();
	if(idx == ha.size()) return false;
	else return ha[idx] == x;
}



for(int i = 1;i + len - 1 <= len1;i++)
		{
			if(check(i,i + len - 1)) cnt++;
		}

        最后AC代码:

#include<bits/stdc++.h>

using namespace std;

typedef unsigned long long ULL;

const int N = 1e7 + 10,P = 131;		//N为1e6 + 10时候会段错误,所以N为1e7 + 10 

ULL h[N],h1[N],p[N];	//两个哈希函数和一个p进制数组,p进制数组存储的是每一个字符串转化为p进制的数 
char s[N],str[N];		//一个是t字符串,一个是要查询的字符串 
int n;					//查询的次数 
vector<ULL> ha;			//存储t字符串中的每一个循环同构字符串 

//h[N]是t字符串的哈希值,h1[N]是要查询的字符串的存储的哈希值 

void init(int len)		//对t字符串进行初始化 
{
	p[0] = 1,h[0] = 1;	
	
	for(int i = 1;i<=2 * len;i++)	//因为是t的循环同构字符串,所以t字符串在相当于一个环,那么    //就破环成链。破环后的t字符串的长度是原长度的2倍 
	{
		p[i] = p[i-  1] * P;		//转换为p进制 
		h[i] = h[i - 1] * P + str[i];	//存储哈希值 
	}
}

//对要查询的字符串进制初始化 
void init1(int len)
{
	p[0] = 1,h1[0] = 1;
	
	for(int i = 1;i<= len;i++)
	{
		
		h1[i] = h1[i - 1] * P + s[i];
	}
}

//求t字符串的循环同构的哈希值 
ULL get(int l,int r)
{
	return h[r] - h[l - 1] * p[r - l + 1];
}

//求要查询的字符串的子串(长度为原t字符串的长度)的哈希值 
ULL get1(int l,int r)
{
	return h1[r] - h1[l - 1] * p[r - l + 1];
}


//检查 查询的字符串的子串的哈希值是否在t字符串的哈希值中出现过 
bool check(int l,int r)
{
	ULL x = get1(l,r);	
    //得到要查询的字符串的子串(长度为原t字符串的长度)的哈希值  
	ULL idx = lower_bound(ha.begin() , ha.end(),x) - ha.begin(); 

    //loer_biund()返回的是一个迭代器,但将返回的值 - ha.begin()那么可以近似的看为返回的是下标	

	//lower_bound() 是一个二分查找函数,用于在已排序的序列中查找第一个不小于(即大于或等于)给定                
    //值的元素。它返回该元素的迭代器。如果序列中所有元素都小于给定值,则返回尾后迭代器(即     
    //end())。

	if(idx == ha.size()) return false;		
    //因为如果没有查询到返回的是hash.end(),所以只要判断返回值是不是为ha.size() 

	else return ha[idx] == x;	

	//lower_bound() 找的是 大于或等于x的元素,而本提是t的循环同构于要查询的字符串的子串匹配,所        
    //以当两者相等是返回true 
}

int main()
{
	cin >>str + 1 >> n;	//从1开始存储 
	
	int len = strlen(str + 1);
	for(int i = 1;i<=len;i++) str[i + len] = str[i];	//对t数组破环成链 
	
	init(len);	//对t字符串进行哈希存储 
	
	for(int i = 1;i<=len;i++)
	{
		ha.push_back(get(i,i + len - 1));		//存储t字符串所有的循环同构字符串 
	}
	
	sort(ha.begin() ,ha.end() );		//因为要用到二分查询,所以要排序 
	
	while(n --)
	{
		int cnt = 0;		//有多少个子串可以匹配 
		cin >>s + 1;
		int len1 = strlen(s + 1);
		init1(len1);
		
		for(int i = 1;i + len - 1 <= len1;i++)		//枚举s字符串,i+len - 1不能超过s字符串    
    //的长度 
		{
			if(check(i,i + len - 1)) cnt++;		//返回true,那么可以匹配 
		}
		cout <<cnt<<endl;
	}
	
	return 0;
	
 } 

        

        

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值