字符串乱记
就是一些关于字符串的笔记,没什么别的。
毫无顺序的目录
- 最小表示法
- Manacher
- trie
最小表示法#
定义:#
首先要了解一个东西叫循环同构。
形式化定义为:
当字符串
则称
举一个通俗易懂的例子:
例如有一个字符串:
它的循环同构有
至于最小表示就是它与它所有循环同构中字典序最小的字符串。
做法#
考虑
首先为了防止越界的分类讨论,可以将字符串进行倍长。
然后设置两个指针,表示最小循环的起始位置,暴力求出最长公共前缀,即
接着比较
假设
点击查看代码
int l = 1 , r = 2;
while(l <= n && r <= n)
{
int k = 0;
while(k <= n && s[l + k] == s[r + k])
++k;
if(s[l + k] < s[r + k])
r += k + 1;
else l += k + 1;
r += (r == l);
}
一道例题#
这确实是一道很好的例题,可惜太弱了,不会做。
以后做了,再补吧。
Manacher#
算法步骤#
一个短小,精悍还贼快的一个算法。
重点在于理解其实也不难理解
首先考虑对于原字符串进行一些改变。
我们发现,对于一个回文字符串而言,它有两种形式:
即中心轴是一个点或者一个空位,我们考虑在每一个字符中间加一个板子(就是随意一个原字符串不会出现的字符),就可以避免分类讨论了。
下文中的操作,都是在新字符串上进行操作的。
记
记录前缀中回文串右端点的最大值
由于回文的对称性我们发现,如果当前扫到的节点在此时记录的最大的回文串内。
那么在回文串的另一边的回文半径就与此时节点一样的。
但是,回文串的对称性同样只在回文串的范围内,因此还要特判一下边界。
至于如何求对称点,小学奥数的中点公式:
即:
继承完了之前的直接暴力继续枚举更新即可。
显然
代码很好写
for(int i = 1 , mid = 0 , r = 0;i <= n;i++)
{
l[i] = (i < r ? min(l[mid * 2 - i] , r - i) : 1);
while(s[i + l[i]] == s[i - l[i]]) l[i]++;
if(i + l[i] > r) r = i + l[i] - 1 , mid = i;
ans = max(ans , l[i]);
}
两道例题#
solution
合法的串
代码也很好写
#include <bits/stdc++.h>
using namespace std;
const int N = 2000010;
int t , n , l[N] , vis[N];
char s[N] , s1[N];
inline int read()
{
int asd = 0 , qwe = 1; char zxc;
while(!isdigit(zxc = getchar())) if(zxc == '-') qwe = -1;
while(isdigit(zxc)) asd = asd * 10 + zxc - '0' , zxc = getchar();
return asd * qwe;
}
int main()
{
t = read();
while(t--)
{
scanf("%s" , s1 + 1) , n = strlen(s1 + 1);
s[0] = '~' , s[1] = '*';
for(int i = 1;i <= n;i++) s[i * 2] = s1[i] , s[i * 2 + 1] = '*';
n = n * 2 + 1 , s[n + 1] = '@';
for(int i = 1 , mid = 0 , r = 0;i <= n;i++)
{
l[i] = (i <= r ? min(l[mid * 2 - i] , r - i + 1) : 1);
while(s[i + l[i]] == s[i - l[i]]) l[i]++;
if(i + l[i] > r) r = i + l[i] - 1 , mid = i;
}
for(int i = n;i >= 1;i--)
{
if(i + l[i] - 1 == n) vis[i] = 1;
else if(i - l[i] + 1 == 1 && vis[i + l[i] - 2]) vis[i] = 1;
}
for(int i = 1;i <= n;i++)
{
if(vis[i] && s[i] >= 'a' && s[i] <= 'z') printf("%d " , i / 2);
vis[i] = 0 , s[i] = ' ';
}
puts("");
}
return 0;
}
solution
利用
代码还是很好写
#include <bits/stdc++.h>
using namespace std;
const int N = 200010;
int n , ans , l[N] , f1[N] , f2[N];
char s[N] , s1[N];
inline int read()
{
int asd = 0 , qwe = 1; char zxc;
while(!isdigit(zxc = getchar())) if(zxc == '-') qwe = -1;
while(isdigit(zxc)) asd = asd * 10 + zxc - '0' , zxc = getchar();
return asd * qwe;
}
int main()
{
scanf("%s" , s1 + 1) , n = strlen(s1 + 1);
s[0] = '~' , s[1] = '*';
for(int i = 1;i <= n;i++) s[i * 2] = s1[i] , s[i * 2 + 1] = '*';
n = n * 2 + 1;
for(int i = 1 , mid = 0 , r = 0;i <= n;i++)
{
l[i] = i < r ? min(r - i + 1 , l[mid * 2 - i]) : 1;
while(s[i + l[i]] == s[i - l[i]]) l[i]++;
if(i + l[i] > r) r = i + l[i] - 1 , mid = i;
f1[i + l[i] - 1] = max(l[i] - 1 , f1[i + l[i] - 1]);
f2[i - l[i] + 1] = max(l[i] - 1 , f2[i - l[i] + 1]);
}
for(int i = 2;i <= n;i++) f1[i] = max(f1[i] , f1[i + 2] - 2);
for(int i = 2;i <= n;i++) f2[i] = max(f2[i] , f2[i - 2] - 2);
for(int i = 1;i <= n;i++) if(f1[i] && f2[i]) ans = max(ans , f1[i] + f2[i]);
cout << ans;
return 0;
}
trie#
算法步骤#
这个东西还好意思要算法步骤吗?
感觉
一道例题#
solution
直接线段树套
代码不是很好写了,但也不难
#include <bits/stdc++.h>
using namespace std;
const int N = 40000010;
int n , m , cnt;
inline int read()
{
int asd = 0 , qwe = 1; char zxc;
while(!isdigit(zxc = getchar())) if(zxc == '-') qwe = -1;
while(isdigit(zxc)) asd = asd * 10 + zxc - '0' , zxc = getchar();
return asd * qwe;
}
struct Trie
{
int c[N][2] , sum[N];
inline void update(int k , int x , int tim)
{
for(int i = 20;i >= 0;i--)
{
int tmp = (x >> i) & 1;
// cout << " :: 1 " << k << " " << tmp << endl;
k = c[k][tmp] ? c[k][tmp] : (c[k][tmp] = ++cnt);
sum[k] = max(sum[k] , tim);
}
}
inline int ask(int k , int x , int tim)
{
int res = 0;
// cout << k << " " << x << " " << tim << endl;
for(int i = 20;i >= 0;i--)
{
int tmp = (x >> i) & 1;
// cout << " :: 2 " << k << " " << tmp << endl;
if(c[k][tmp ^ 1] && sum[c[k][tmp ^ 1]] >= tim) res += 1 << i , k = c[k][tmp ^ 1];
else if(sum[c[k][tmp]] >= tim) k = c[k][tmp];
else return res;
}
return res;
}
}t;
inline void change(int p , int ls , int rs , int num , int tim , int id)
{
t.update(p , num , tim);
if(ls == rs) return;
if((ls + rs) / 2 >= id) change(p * 2 , ls , (ls + rs) / 2 , num , tim , id);
else change(p * 2 + 1 , (ls + rs) / 2 + 1 , rs , num , tim , id);
}
inline int get(int p , int l , int r , int ls , int rs , int num , int tim)
{
if(l <= ls && rs <= r) return t.ask(p , num , tim);
int sum = 0;
if((ls + rs) / 2 >= l) sum = max(sum , get(p * 2 , l , r , ls , (ls + rs) / 2 , num , tim));
if((ls + rs) / 2 < r) sum = max(sum , get(p * 2 + 1 , l , r , (ls + rs) / 2 + 1 , rs , num , tim));
return sum;
}
int main()
{
n = read() , m = read() , cnt = n * 4;
for(int i = 1;i <= n;i++)
{
int x = read();
change(1 , 1 , n , x , 1e9 , i);
}
int tim = 0;
for(int i = 1;i <= m;i++)
{
int opt = read();
if(opt == 0)
{
++tim;
int s = read() , v = read();
change(1 , 1 , n , v , tim , s);
}
else
{
int l = read() , r = read() , x = read() , d = read();
cout << get(1 , l , r , 1 , n , x , max(tim - d + 1 , 0)) << endl;
}
}
return 0;
}
作者:JiaY19
出处:https://www.cnblogs.com/JiaY19/p/15935914.html
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)