20230716

T1 动物园

题意:

定义num[i]为一个序列前i位不重叠公共前后缀的个数,给定序列,对于每一个i求num[i]

解法:

先不考虑前后缀重不重叠的问题,那么当且仅当\(next[i]\)\(next[next[i]]\)\(next[next[next[i]]]......\)是这个前缀串i的公共前后缀。
如何去除有重叠的?

首先显然\(next[i] < i\)

也就是说,一旦有一个递归了\(n\)层的\(next\),比原前缀\(i\)的长度的一半要小,那么这个\(next\)的递推出的答案\(ans\)就是\(i\)\(num\)

一个问题
假如我们拿到的串是1e6个'a',那么上面那个算法就会被卡成\(O(n^2)\)

那么我们需要做一个优化,来解决这个问题,而解决问题的核心就是:减少重复递归

如同求next时一样的方法,我们将递归用的变量j的值不更新,这样,求完了i的答案以后,j的位置一定在$ i / 2$
的左边,也就是它已经满足要求了,再递归求解。

T2 字符串的匹配

题意:

定义 \({ai}, {bi}\) 相同,当且仅当它们离散化之后相同。
给定 \(a1, . . . an\) 以及 \(b1, . . . bm\),找到所有位置 \(p\) 使得 \(ap...ap+m-1\)\({bi}\) 相同。\(m ≤ n ≤ 1e5\)

解法:

我的做法,有些逆天。用一个桶排序。再套上一个哈希滑动窗口,每次检查直接乘上桶的权值即可。

T3 Sza-Template

题意:

\(Byteasar\) 想在墙上涂一段很长的字符, 他为了做这件事从字符的前面一
段中截取了一段作为模版。然后将模版重复喷涂到相应的位置后就得到
了他想要的字符序列。一个字符可以被喷涂很多次, 但是一个位置不能
喷涂不同的字符。做一个模版很费工夫, 所以他想要模版的长度尽量小,
求最小长度是多少。\(n ≤ 10e6\)

解法:

真的我这个解法打出来就是误人子弟

T4 Country

题意:

\(n ≤ 26\) 个字符串变量,它们可以包含其他的字符串变量,也可以包
含小写字母(这些变量用大写字母表示)。
举个栗子:\(A=greatglorycorrect\)\(B=xx\)\(C=leadusgo\)\(D=ABC\)\(E=DDDDdjh\)\(F=EEEEEgoodbye\)
数据保证定义是无环的。
给定一个小写字母组成的模式串,求某一个变量所代表的字符串里这个
模式串出现了几次。模式串长度, 每条描述的长度 \(≤ 100\)

解法:

首先我们设dp[i][j] 表示当前处理到字符串i(还未与i匹配),上一次模板串匹配到j位时的匹配次数。我们就发现一件事,就是这个东西显然是可以记忆化的。我们用dfs(i,j)深搜当前的dp[i][j](为了方便判断是否搜过先把dp数组设置成-1)。

首先我们遍历整个字符串i。对于所有大写字母,直接dfs到下一层就可以了。对于小写字母,我们直接做KMP板子就行了。

炸了的代码有大神看到请帮忙看看,十分感谢

#include<bits/stdc++.h>
using namespace std;
const int mod=10000;
int n;
int t;
char tt;
char tol[200];
string mean[30];
char c[30][100000];
int sz[200];
string comp;
int nxt[200];
int dp[30][100000];
//dp[i][j] 表示当前处理到字符串i(还未与i匹配),上一次模板串匹配到j位时的匹配次数。
int pos[30][100000];
//pos[i][j] 表示当前处理到字符串i(还未与i匹配),上一次模板串匹配到j位,匹配结束后模板串的位置。
void get_nxt(string s1){
	int i=0,j=-1;
	nxt[0]=-1;
	while(i!=s1.size()){
		if(s1[i]==s1[j] || j==-1){
			i++;
			j++;
			nxt[i]=j;
		}
		else{
			j=nxt[j];
		}
	}
	for(int i=1;i<=30;i++){
		for(int j=1;j<=100000;j++){
			dp[i][j]=-1;
		}
	}
}

int dfs(int i,int j){
	if(dp[i][j]!=-1) return dp[i][j];
	dp[i][j]=0;
	int x=j+1;
	for(int k=1;k<=sz[i];k++){
		if(c[i][k]>='A' && c[i][k]<='Z'){
			int id=c[i][k]-'A'+1;
			dp[i][j]+=dfs(id,x);
			dp[i][j]%=mod;
			x=pos[id][x]+1; 
		}
		else{
			int p=x;
			while(p && comp[p]!=c[i][k]) p=nxt[p];
			if(comp[p]==c[i][k]) p++;
			x=p;
			if(x==comp.size()){
				dp[i][j]++;
				dp[i][j]%=mod;
			} 
		}
	}
	pos[i][j]=x;
	return dp[i][j];
}

int main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin>>n;
	cin>>tt;
	t=tt-'A'+1;
	for(int i=1;i<=n;i++){
		cin>>mean[i];
		sz[i]=mean[i].size()-2;
		for(int j=1;j<mean[i].size();j++){
			c[i][j]=mean[i][j+1];
		}
	}
	/*
	for(int i=1;i<=n;i++){
		for(int j=1;j<=sz[i];j++){
			cout<<c[i][j];
		}
		cout<<endl;
	}*/
	cin>>comp;
	get_nxt(comp);
	dfs(t,0);
	cout<<dp[t][0]<<endl;
	return 0;
} 

T9 Substrings in a String

题意:

单点修改,每次问一个串 \(Ti\)\(S[l,r]\) 中的出现次数.
\(n, q\),\(∑|Ti| <=1e5\)

解法:

用bitset并就行了,我觉得关键点反而是bitset的操作。

count(): 返回 true 的数量。

size(): 返回 bitset 的大小。

test(pos): 它和 vector 中的 at() 的作用是一样的,和 [] 运算符的区别就是越界检查。

any(): 若存在某一位是 true 则返回 true,否则返回 false。

none(): 若所有位都是 false 则返回 true,否则返回 false。

all():C++11,若所有位都是 true 则返回 true,否则返回 false。

set(): 将整个 bitset 设置成 true。

set(pos, val = true): 将某一位设置成 true/false。

reset(): 将整个 bitset 设置成 false。

reset(pos): 将某一位设置成 false。相当于 set(pos, false)。

flip(): 翻转每一位。(0\leftrightarrow1,相当于异或一个全是 1 的 bitset)

flip(pos): 翻转某一位。

to_string(): 返回转换成的字符串表达。

to_ulong(): 返回转换成的 unsigned long 表达(long 在 NT 及 32 位 POSIX 系统下与 int 一样,在 64 位 POSIX 下与 long long 一样)。

to_ullong():C++11,返回转换成的 unsigned long long 表达。

posted @ 2023-07-27 14:45  星河倒注  阅读(23)  评论(0)    收藏  举报