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 表达。

浙公网安备 33010602011771号