牛客 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;
	
 } 

        

        

相关推荐

  1. 周赛 Round 36----->C.小红白色字符串

    2024-04-26 00:26:01       45 阅读
  2. 》-C小红字符串构造

    2024-04-26 00:26:01       41 阅读
  3. 储物点距离

    2024-04-26 00:26:01       36 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-04-26 00:26:01       98 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-04-26 00:26:01       106 阅读
  3. 在Django里面运行非项目文件

    2024-04-26 00:26:01       87 阅读
  4. Python语言-面向对象

    2024-04-26 00:26:01       96 阅读

热门阅读

  1. CDN的原理

    2024-04-26 00:26:01       25 阅读
  2. 低空经济和无人机

    2024-04-26 00:26:01       22 阅读
  3. MySQL中CHANGE REPLICATION FILTER 语句详解

    2024-04-26 00:26:01       29 阅读
  4. Eclipse debug时有几个常用的快捷键非常实用

    2024-04-26 00:26:01       34 阅读
  5. 代码随想录算法训练营day45

    2024-04-26 00:26:01       32 阅读
  6. C语言中的逻辑运算

    2024-04-26 00:26:01       27 阅读
  7. 3. 4XX (Client Error 客户端错误状态码)

    2024-04-26 00:26:01       27 阅读
  8. 20240424 diary

    2024-04-26 00:26:01       28 阅读
  9. 数据结构PT2——堆栈/队列

    2024-04-26 00:26:01       34 阅读
  10. 探索常见经典目标检测算法:从YOLO到Faster R-CNN

    2024-04-26 00:26:01       34 阅读
  11. C++ set、map

    2024-04-26 00:26:01       24 阅读