字符串专题讲解

VBA20自定义函数 怎么在日常使用带有自定义函数的工作本、自定义函数几个常用关键词、几个返回实例 阅读详情

最近教练叫我去讲字符串专题,于是来写一写这方面的内容

主要就讲以下几个吧:

1.Kmp 

2.Extended Kmp

3.Trie

4*.AC Automation (Trie Graph)

5*.String Hash

6.Suffix Array

7*.Suffix Automation

8*.Suffix Tree

9.manacher

10*.Palindrome Automation

11*.Suffix Balanced Tree


前面三个会讲得比较简单因为两个kmp不常用(基本可以用hash代替),Trie基本人人都会了

正题开始:

KMP

当时刚刚学的时候觉得它是一种十分神奇的算法,不过自从学了hash以后就再也没怎么打过

kmp基本思想就是重复利用以前处理过的信息

我们需要对c求一个next数组,next[i]表示最大的j使得c[0..j]=c[i-j+1..i]且j<i

假设我们已经有了这个next数组,那么匹配过程就会非常简单

当我们文本串匹配到i,模板串匹配到j时如果失配了,我们就可以将j和next[j]对齐重新开始匹配

为什么?因为我们匹配到了第j位,所以显然有s[i-next[j]..i]=c[j-next[j]+1,j],而又有c[0..next[j]]=c[j-next[j]+1,j]

所以c[0..next[j]]这一段我们不需要重新匹配一次就可以知道它们肯定是配对的

如何求next,这个过程相当于和自己做匹配一样,复杂度可以证明O(n),此处略,下面直接上code了

int nt[N];
void KMP(char* s,char* c,int& t,int* ans){
	int n=strlen(c),m=strlen(s);
	for(int i=1,j;i<n;++i)
		for(j=i;j;)
			if(c[j=nt[j]]==c[i]) { nt[i+1]=j+1; break; }
	for(int i=0,j=0;i<m;++i){
		if(j<n && s[i]==c[j]) ++j;
		else while(j) if(c[j=nt[j]]==s[i]){ ++j; break; }
		if(j==n) ans[++t]=i-n+1;
	}
}

ok,下面讲讲扩展KMP

Extended Kmp

这个东西呢比较鸡肋,所以这里随便说说思路,有兴趣的同学自己去网上搜来学

算法作用:在线性时间内求出一个串对另一个串的每个后缀的最长公共前缀

假设两个串为s和p,要求p和每个s的后缀的最长公共前缀,我们可以先求出p和自己每个后缀的最长公共前缀(记为A)

类似kmp算法,利用前面信息,假设我们正在计算A[i],我们可以找到一个k∈[1..i-1]使得k+A[k]尽可能大

显然有p[1..A[k]]=p[k..k+A[k]-1],于是我们就可以得到p[i..k+A[k]-1]=p[i-k+1..A[k]]

分两种情况

1.若i+A[i-k+1]-1<k+A[k]-1那么A[i]=A[i-k+1]

2.否则,暴力匹配A[i]

复杂度可以证明O(n),此处略,code略(可以自行百度)


Trie

这基本是所有人字符串入门的必修课

Trie非常好理解,其实就是将一堆字符串按位拆开而已

Trie的基本操作应该大家都很熟悉了,这里只是随便讲讲

一开始整颗trie只有一个根,每次插入字符串的时候,都向对应的儿子走,如果儿子为空就新建节点

在字符串结束的那个节点打上标记,查询和删除和插入过程基本一样,就是查询是否有标记和清除标记而已

这里给一个模板(随手打的不要吐槽,这里假设全部字符都是大写字母)

int cnt,c[1000010],ch[1000010][26];
inline void insert(char* s,int x=1){
    for(;*s;++s){
        *s-='A';
        if(!ch[x][*s]) ch[x][*s]=++cnt;
        x=ch[x][*s];
    }
    ++c[x];
}
inline bool find(char* s,int x=1){
    for(;*s;++s){
        *s-='A';
        if(!ch[x][*s]) return 0;
        x=ch[x][*s];
    }
    return c[x];
}
inline void remove(char* s,int x=1){
    for(;*s;++s){
        *s-='A';
        if(!ch[x][*s]) return;
        x=ch[x][*s];
    }
    if(c[x]) --c[x];
}

ok,开始进入真正的正题了


Ac自动机

这是干什么的?当然不是用来自动切题的机器

我们考虑,如果做要对k个模板串做匹配,那么kmp的效率就会低下(O((n+m)k))

但是Ac自动机可以在有k个模板串的情况下保证复杂度为O(n+m)

那么他是如何完成的呢?这里我们就要将上面讲的两个东西结合起来了

首先,我们有很多个字符串,我们考虑将它们插入到一个trie里面

让后拿文本串在这个trie上面查询,我们还是要考虑失配情况

不同的是,这里我们没有办法直接求next数组,所以我们考虑失配时如何操作

我们对于每个节点x,建立一个fail指针,这个fail指针指向trie树的一个节点y,满足Str(y)是Str(x)的后缀(没有满足的就指向根)

这里Str(x)表示从根到x的路径所组成的字符串

那么当我们匹配到x时,如果下一位失配,我们可以立即跳到fail[x]继续匹配(因为str(y)和str(x)的后半段相等)

是不是和kmp很像?

现在我们需要考虑如何建立fail指针

我们发现,对于一个节点x,他的fail指针如果不是根,那么一定是他的父亲节点father[x]的fail的一个子节点,或者是fail[fail[father[x]]的一个子节点,或者是。。。一直到root为止

于是我们考虑用bfs:

对于一个节点x,枚举每个儿子son[x][c],我们看fail[x]是否有儿子c

如果有那么fail[son[x][c]]=son[fail[x]][c],否则令x=fail[x]直到x=root,这时fail[son[x][c]]=root

好的到这里,我们发现一个问题:我们每次去求fail[son[x][c]]的时候呢,我们都要跑一条‘fail’链

这样很显然是有些浪费的(虽然这样写,最后复杂度也是O(n)的,但是常数稍大)

所以我们考虑做一件这样的事(其实就是将Ac自动机建成Trie图)

因为我们失配的条件是son[x][str[i]]=NULL,那么这样很显然是比较浪费的

我们不如将其直接指向son[fail[x]][str[i]],这样就可以简化x=fail[x]的过程,直接继续匹配就好了

注意如果son[root][c]=NULL那么我们要将其变成root,即s[root][c]=root(显然,因为fail[root]=root)

下面放一张图给大家体会一下两者的区别


看到了吗,如果我们匹配00,那么走到左边那个蓝色节点时,按照fail指针会先走去根再走下来

但是如果是trie图就直接不用走上去了,直接省略了这一步,这样大大简化了代码也提高了匹配效率

这里再说一下匹配的具体过程:

每次从根开始,匹配到一个节点x后,我们沿着它的fail指针检查这些节点是否有被标记为结尾节点(这一步不可省略,因为如果节点x被匹配成功,那么x沿fail一直走的路径上的所有节点也会匹配成功,所以都要统计)让后令x=son[x][str[i]](这里因为我们建立了Trie图,所以不会有任何节点的son指向NULL,最多只会指向root)

这里应该给一些题目了,经典题目就有HDU2222 和HDU2896都是模板题

这里讲一讲hdu2896,题意就是给你n个模板串和m个文本串,要求出每个文本出现了哪些模板串

非常的简单,建立Trie图直接跑就好了,非常推荐用数组板Trie图的写法,因为比ac自动机还简单

#include<queue>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 100010
using namespace std;
char s[N];
int fail[N],son[N][128],c[N],cnt=1,n,T=0;
inline void insert(char* s,int pos){
    int x=1;
    for(;*s;++s)
        x=son[x][*s]?son[x][*s]:son[x][*s]=++cnt;
    c[x]=pos;
}
inline void build(){
    queue<int> q;
    for(int i=0;i<128;++i)
        if(!son[1][i]) son[1][i]=1;
        else q.push(son[1][i]),fail[son[1][i]]=1;
    for(int x;!q.empty();q.pop()){
        x=q.front();
        for(int i=0;i<128;++i)
            if(!son[x][i]) son[x][i]=son[fail[x]][i];
        else q.push(son[x][i]),fail[son[x][i]]=son[fail[x]][i];
    }
}
inline bool query(char* s){
    bool vis[510]={0},v=0;
    for(int x=1;*s;++s){
        x=son[x][*s];
        for(int j=x;j!=1;j=fail[j])
            if(c[j]) vis[c[j]]=v=1;
    }
    if(!v) return 0;
    printf("web %d:",T);
    for(int i=1;i<=n;++i) 
        if(vis[i]) printf(" %d",i);
    puts(""); return 1;
}
int __18520(){
    if(scanf("%d",&n)<0) return 0; 
    for(int i=1;i<=n;++i){
        scanf("%s",s); insert(s,i);
    }
    build(); int ans=0,m; scanf("%d",&m); 
    for(T=1;T<=m;++T){
        scanf("%s",s); ans+=query(s);
    }
    printf("total: %d\n",ans); return cnt=1;
}
int main(){ while(__18520()); }

Hash
为什么这么迟才讲?因为这个东西非常重要!比上面那些加起来都有用!

这个东西是字符串中的三大神器之一(另外两个是后缀自动机和后缀平衡树)

是这样的,我们回到一个最简单的问题,单模板串匹配

不是已经讲了KMP了吗,为什么要讲这个呢?当然是这个东西好写啊

我们考虑暴力匹配,复杂度是O(nm),为什么这么慢呢,主要问题就是在比较上,要O(m)的时间

假如我们可以O(1)比较不就好了吗,对!我们可以使用哈希

我们将一段字符串看成一个k进制数,这里k是字符集的大小

那么我们现在考虑如何快速求出一段字串对应的这个数,我们可以用前缀和

我们令hash(l,r)表示字串[l,r]的哈希值,那么显然hash(l,r)=hash(1,r)-hash(1,l-1)*k^(r-l+1)

而hash(1,r)和k^(r-l+1)可以用前缀和预处理,我们令h[i]=hash(1,i),显然h[i]=h[i-1]*k+str[i]

注意这里我们是不能存下这个数的,所以要取模或者让其自然溢出,当然是自然溢出好啦

让后我们就可以得到一个非常简单的模板

#define BASE 27
#define LL long long
char s[N]; LL h[N],bas[N];
LL gH(int l,int r){ return h[r]-h[l-1]*bas[r-l+1]; }
void init_hash(){
	for(int i=1;i<=n;++i){ 
		bas[i]=bas[i-1]*BASE;
		h[i]=h[i-1]*BASE+s[i]-'a';
	}
}
是不是可以和你的kmp比短了,我们可以立即写一个匹配的code,只需要一个循环,每次比较hash(i,i+n)和模板串的hash值是否相等就好了,这里略

好了我们马上就可以将其实现扩展kmp的功能

因为我们要的是求最长公共前缀,所以我们对两个字符串都预处理出hash值,每次枚举开头,让后二分就好了,复杂度n lg n

你可能会说这个效率不如扩展kmp啊,但是如果你喜欢打扩展kmp那我也没办法咯

附上一个二分求lcp的完整代码:

#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 1000010
#define L long long
using namespace std;
char s[N]; int n;
L ans=0,b[N]={1},h[N];
inline L gH(int l,int r){ return h[r]-h[l-1]*b[r-l+1]; }
int dijk(int x){
	int l=-1,r=n-x;
	for(int m;l<r;){
		m=l+r+1>>1;
		if(gH(1,m+1)==gH(x,x+m)) l=m;
			else r=m-1;
	}
	return l+1;
}
int main(){
	freopen("gene.in","r",stdin);
	freopen("gene.out","w",stdout);
	scanf("%s",s+1); n=strlen(s+1);
	for(int i=1;i<=n;++i){
		b[i]=b[i-1]*27;
		h[i]=h[i-1]*27+s[i]-'a';
	}
	for(int i=1;i<=n;++i) ans+=dijk(i);
	printf("%lld\n",ans);
}
好了我们来讲讲比较难的题目吧

spoj1811:LCS

这个题我们讲SA和SAM时还会用到,不过这里先讲讲hash的做法

题意:给你两个字符串A,B求最长公共子串

注意这个不是那个dp题,O(n^2)是肯定过不了的

我们需要考虑如何在O(n lg n)的时间内完成

考虑二分答案:我们每次二分出一个Len

对于每个i∈[1,n-Len],对于A求出hash(i,i+Len)并存在一个数组里面,记为A',用类似的方法求出B'

让后我们看A'和B'有没有相同的数,如果有说明LCS>=Len 否则LCS<Len

这样就是O(n lg^2 n)的

诶,因为spoj太慢了上不去,网上也没找到用这个方法来写的code,这里就不放code了,下一个专题


后缀数组 Suffix Array

这个东西嘛本来是一个神器,不过当后缀自动机出来以后就没那么神了

感觉也是比较鸡肋,和下面的sam比起来嘛,复杂度高,功能还不如它强大,而且还容易写错,呃。。。

网上流传的方法,一般就是两种复杂度,倍增算法O(n lg n),dc3算法O(n)

但是我还会三种其他复杂度的算法,分别是:直接快排O(n^2 lg n) 直接基排O(n^2) 和带hash的快排O(n lg^2 n)

个人最推荐带hash的快排,好写,常数小,下面口胡一下

我们发现直接快排的问题就在于比较的代价最坏是O(n)的,所以我们要加速比较,这时候就可以用哈希

还记得我们用哈希求两个字符串的lcp吗,我们对于两个后缀x和y,用二分求出它们lcp的长度,让后比较下一位的大小就可以得出两个后缀的大小了

因为二分做一次复杂度是O(lg n)的,快排本身要n lg n所以复杂度就是O(n lg^2 n),给个code吧

#define BASE 27
#define LL long long
char s[N]; LL h[N],bas[N]; int sa[N],r[N],H[N];
LL gH(int l,int r){ return h[r]-h[l-1]*bas[r-l+1]; }
void init_hash(){
	for(int i=1;i<=n;++i){ 
		bas[i]=bas[i-1]*BASE;
		h[i]=h[i-1]*BASE+s[i]-'a';
	}
}
inline bool cmp(int x,int y){
	int l=-1,r=n-max(x,y);
	for(int m;l<r;){
		m=l+r+1>>1;
		if(gH(x,x+m)==gH(y,y+m)) l=m;
		else r=m-1;
	}
	return s[x]<s[y];
}
void buildSa(){
	init_hash();
	for(int i=1;i<=n;++i) sa[i]=i;
	sort(sa+1,sa+1+n,cmp);
	for(int i=1;i<=n;++i) r[sa[i]]=i;
	for(int i=1,j,k=0;i<=n;H[r[i++]]=k){
		if(k) --k;
		if(r[i]>1) for(j=sa[r[i]-1];s[i+k]==s[j+k];++k);
	}
}
这里说一下那个H数组,这个就是大名鼎鼎的height数组

height[i]表示的是Sa[i]和Sa[i-1]的最长公共前缀(即两个排名相邻的后缀的lcp)

这里我们说一下如何计算height[i],首先有一点是显然的,height[r[1]]=0且height[rank[i]]>=height[rank[i-1]]-1

所以我们只需要按照height[rank[1]],height[rank[2]]...的顺序计算即可

目前我见过的题目里面,似乎只有一道是必须要用SA来做的(而且我还没切掉),其他都可以用SAM,所以这里先不放题目,如果想练习一下,可以试试做上面那个LCS,这个题本来就是应该用SA做的

下面准备开始飙车了,Sam就要出场了!

Suffix Automation 后缀自动机

这是个啥?我们要先说说自动机

有限状态自动机(FSM "finite state machine" 或者FSA "finite state automaton" )是为研究有限内存的计算过程和某些语言类而抽象出的一种计算模型。有限状态自动机拥有有限数量的状态,每个状态可以迁移到零个或多个状态,输入字串决定执行哪个状态的迁移。有限状态自动机可以表示为一个有向图。有限状态自动机是自动机理论的研究对象。

有限状态自动机是具有离散输入和输出的系统的一种数学模型。

其主要特点有以下几个方面:

– (1)系统具有有限个状态,不同的状态代表不同的意义。按照实际的需要,系统可以在不同的状态下完成规定的任务。

– (2)我们可以将输入字符串中出现的字符汇集在一起构成一个字母表。系统处理的所有字符串都是这个字母表上的字符串。

– (3)系统在任何一个状态下,从输入字符串中读入一个字符,根据当前状态和读入的这个字符转到新的状态。

– (4)系统中有一个状态,它是系统的开始状态。

– (5)系统中还有一些状态表示它到目前为止所读入的字符构成的字符串是语言的一个句子。

形式定义

· 定义:有限状态自动机(FA—finite automaton)是一个五元组:
– M=(Q, Σ, δ, q0, F)
· 其中,
– Q——状态的非空有穷集合。∀q∈Q,q称为M的一个状态。
– Σ——输入字母表。
– δ——状态转移函数,有时又叫作状态转换函数或者移动函数,δ:Q×Σ→Q,δ(q,a)=p。
– q0——M的开始状态,也可叫作初始状态或启动状态。q0∈Q。
– F——M的终止状态集合。F被Q包含。任给q∈F,q称为M的终止状态。

上面其实是一堆废话,我们引用CLJ的大白话来解释一下

有限状态自动机的功能是识别字符串,令一个自动机A,若它能识别字符串S,就记为A(S)=True,否A(S)=False。

自动机由五个部分组成,alpha:字符集,state:状态集合,init:初始状态,end:结束状态集合,trans:状态转移函数。

如果trans(s,ch)这个转移不存在,为了方便,不妨设其为null,同时null只能转移到null。

那么自动机A能识别的字符串就是所有使得trans(init,x) ⊂end的字符串x,令其为Reg(A)。
从状态s开始能识别的字符串,就是所有使得trans(s,x) ⊂end的字符串x,令其为Reg(S)

好了,这里就可以给出后缀自动机的定义了:

字符串S的后缀自动机suffix automaton(以后简记为SAM)是一个能够识别S的所有后缀的自动机

所以有SAM(x)=true 当且仅当x为S的后缀,同时也可以看出后缀自动机也可以用来识别S的所有子串

那我们要开始考虑如何实现Sam了,首先最暴力的方法,肯定是将整个字符串的后缀插入到一个Trie里面,这样复杂度是O(n^2)的

所以我们要建立一个最简形式的自动机,使得其复杂度是O(n)的

我们的构造算法是Online的,也就是从左到右逐个添加字符串中的字符。依次构造SAM。

这个算法实现相比后缀树来说要简单很多,尽管可能不是非常好理解。

让我们先看一下性质:

状态s,转移trans,初始状态init,结束状态集合end。
 母串S,S的后缀自动机SAM(Suffix Automaton的缩写)。
 Right(str)表示str在母串S中所有出现的结束位置集合。
 一个状态s表示的所有子串Right集合相同,为Right(s)。
 Parent(s)表示使得Right(s)是Right(x)的真子集,并且Right(x)的大小最小的状态x。
 Parent函数可以表示一个树形结构。不妨叫它Parent树

 一个Right集合和一个长度定义了一个子串。

 对于状态s,使得Right(s)合法的子串长度是一个区间,为[Min(s),Max(s)]
 Max(Parent(s)) = Min(s)-1。
 SAM的状态数量和边的数量,都是O(N)的。
 那么如果s出发有标号为x的边,那么Parent(s)出发必然也有。
 同时,对于令f=Parent(s),
 Right(trans(s,c)) ⊆ Right(trans(f,c))。
 有一个很显然的推论是Max(t)>Max(s)

复制不动了,我们直接来看code理解一下吧qwq

char str[N];  
int s[N][26],mx[N],f[N],sz[N];  
int last=1,cnt=1,n,v[N],r[N],ans=0;   
inline int extend(char c){  
    int p=last,np=last=++cnt,q,nq;  
    c-='a'; mx[np]=mx[p]+1; sz[np]=1;  
    for(;p&&!s[p][c];p=f[p]) s[p][c]=np;  
    if(!p) return f[np]=1;  
    q=s[p][c];  
    if(mx[p]+1==mx[q]) f[np]=q;  
    else {  
        nq=++cnt;  
        mx[nq]=mx[p]+1;  
        f[nq]=f[q]; f[q]=f[np]=nq;  
        memcpy(s[nq],s[q],26<<2);  
        for(;p&&s[p][c]==q;p=f[p]) s[p][c]=nq;  
    }  
}  
int build(){  
    scanf("%d%s",&n,str);  
    for(int i=0;i<n;++i) extend(str[i]);  
    for(int i=1;i<=cnt;++i) ++v[mx[i]];  
    for(int i=1;i<=n;++i) v[i]+=v[i-1];  
    for(int i=cnt;i;--i) r[v[mx[i]]--]=i;  
    for(int p,i=cnt;i;--i) sz[f[r[i]]]+=sz[r[i]];   
}  
学习SAM的话,如果不想花很多时间去看懂那个建立的过程证明,还是将模板记下来,学学具体怎么使用

假设我们已经建好了SAM,现在要考虑一下如何使用

首先我们可以对文本串建立SAM来跑字符串匹配,当然这方面,SAM远不如Ac自动机

好的,我们先来看一道难(shui)题  (注意这里的题大部分都可以用SA解决,方法可以自行思考)

tyvj1515:给出一个字符串:求出它不同子串的个数

首先说一下SA的做法吧:Answer=n*(n-1)/2-Σheight[i]

显然子串总数为n*(n-1)/2,而height[i]表示了有多少个子串是重复的,所以直接减去就好了

让后自动机呢?也很简单,Answer=Σmx[i]-mx[f[i]]

其实如果是后缀自动机呢,只需要记下来每个节点的属性是什么,就可以灵活运用了

对于一个节点,有一下几个基本节点:

1.mx表示这个节点的深度,也就是代表这个节点所表示的字符串的长度

2.size表示这个节点所代表的字符串的出现次数

3.parent表示这个节点的父亲节点,但由于Sam不是严格的树形结构,所以这个parent是“上一个可以接受后缀的节点,而不是真正的父亲节点”

让后就很简单了,比如我们可以考虑如何统计一下一个字符串中,出现次数*长度最大的子串

很简单啊,先预处理出size和mx,让后答案就是max{mx[i]*size[i]}了

比如我们还可以计算一个指定的子串的出现次数

这里用倍增算法,我们对parent所组成的一棵“树”做倍增就好了,一直向上跳,直到一个节点满足mx[x]>=len而且mx[f[x]]<len,这个x的size就是答案

配合manacher,后缀自动机还可以完成回文串的相关问题,一会讲了manacher再说

现在来考虑一下上面提到的,LCS的问题,怎么用后缀自动机完成?

其实也非常简单,我们对A做出自动机,让后用B上面跑,跑到一个节点x跑不动了就跳到f[x]那里,让后答案就是最大的mx[x]

显然这样是对的,因为f[x]包含了x的right集合

那如果要求n个字符串的LCS呢?

也很简单,直接记录每个节点最多匹配到几个串,取最大就好了

------------------------------------------------分割线------------------------------------------------------------

好了先休息一下吧

至于例题,多得数不胜数,去看看我的后面几篇文章吧,这里不列举了

------------------------------------------------分割线------------------------------------------------------------

后缀树 Suffix Tree

终于到这里了,SAM的坑就当是填上了吧233333

这里先说一下后缀Trie,很容易理解,就是将字符串的每个后缀插入到一个Trie里面,让后就可以搞很多东西了

然而这东西是O(n^2)的,非常不吼

我们画个图或者随便想想都知道,有很多的路径都是只有一条分支的,这些部分如果能压缩起来就非常好了

于是我们有了Ukkonen算法:点这里

但是这里我们是肯定不会去花时间讲它的,因为比较复杂而且比较慢,不如SAM优秀

接下来这里就讲讲如何用SAM构建后缀树

我们构建SAM后,每个节点都有一个f,那么这就可以构成一棵树,不妨叫他parent树

同时,一个非常惊人的结论:SAM的parent树,和反串的后缀树,是一个东西

具体证明?没有,不过这里可以大概解说一下为什么这样大概是对的

我们考虑自动机上一个节点x,它到根的路径上,一定是原串的一个子串

而他的f节点所代表的那个串,一定是x代表串的后缀,所以如果反过来,那么f[x]在后缀树里面的节点就是x的前缀了,所以这样是可以的(看不懂就跳过去,这里不是很重要。。。)

好了,有了这个性质,我们就可以O(n)构建后缀树了,这要怎么用呢?

其实用处还挺多的,比如我们看一道题:

给出字符串x求它第k大的子串(重复的算一次)

SAM要做肯定很简单吧,建出SAM,求出size[x]=Σsize[son[x][i]] (注意这个size和上面提到的size并不是同一个东西),让后类似于平衡树那样去找就好了

好了现在将题目升级,多次查询第k大的子串?

哈哈哈哈上面的方法不管用了!因为SAM的深度最多为O(n)

那怎么办呢?

我们将反串parent树跑出来,构成后缀树

让后对这个树求dfs序,让后就可以二分了,为什么?

因为dfs序就是SA啊!height就是dfs序里面两个点的LCA啊!

剩下的就可以自己意会了吧,这三者的关系已经非常明显了

回文专题开始!


Manacher

这个东西。。。。。。。

好像有点。。。。。。。

不管了先说说回文串,从名字易得它的定义

现在来考虑如何乱搞

最简单的方法,直接枚举中间点和长度,让后乱搞。。。

当然还有奇怪的方法,正反一个hash让后二分,nlgn 已经很优秀了23333

回到正题,我们还是考虑如何正常求回文串

(可能有些同学以前接触到的manacher是将字符串长度加倍让后每个字符之间插入#的做法,这里不采用这种方法)

假设我们已经求出了前i-1个位置作为回文中心的回文串长度,不妨记为len[1..i-1],那么我们要如何快速求len[i]呢

一个很重要的思想:利用以前的信息

我们可以找到一个len[x]+x最大的x,让后分情况讨论

如果len[x]+x>i,那么len[i]至少为min(len[x]+x-i,len[2*x-i]),否则至少为0

因为如果x作为对称中心,且len[x]+x>i,那么显然len[i]和len[i关于x的对称点]应该是相同的,当然这里还要考虑len[x]+x的限制,所以len[i]=min(len[x]+x-i,len[2*x-i])

当然上面说的是至少,求出答案那就必须要继续匹配一下了,当然这样匹配效率就很高了,最坏也是O(n)的,这也是任何字符串题目的时间下限(因为读入至少就要O(n)了)

证明?没有,但是各位可以手玩一下一个样例:aaaaaaaa让后你就能体会到为什么他是O(n)的了

当然,manacher的好处不仅仅是比较快,它还有一个重要的性质:可以说明一个串中,本质不同的回文串也是O(n)的,这点哈希就没法做到,当然具体要求出有多少还要靠下面的PAM

有了manacher,配合自动机我们就可以做这道题了:APIO2014回文串(做不出来可以看我的博客有题解)

当然解决回文串最好的方法还是,PAM

回文自动机 Palindrome Automation

非常好,终于快结束了

回文自动机其实比后缀自动机还要简单,我们和后缀自动机一样也跳过它的构建过程,而说明一下它的性质和作用

每个节点x有以下属性:
1.len[x]表示x所代表的回文串长度,
注意,一个节点到根的路径上的字符串并不是这个回文串而是这个回文串的一半

2.son[x]表示x的儿子

3.fail[x],也可以叫做father[x]表示x的最小扩充集

4.size[x]表示x的回文串出现次数

而且为了便于分析,我们的PAM有两个根,0和1,一个代表长度为偶数的回文串,一个奇数的回文串,且f[0]=1

下面给出code:【Tsinsen】A1280. 最长双回文串

#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 300010
using namespace std;
char S[N];
int n,m=0,cnt=1,lst=0,g[N],ans=0;
int s[N][26],mx[N]={0,-1},sz[N],f[N]={1};
inline int extend(int c){
	int p=lst,np;
	for(++m;S[m-mx[p]-1]!=S[m];p=f[p]);
	if(!s[lst=p][c]){
		np=++cnt; mx[np]=mx[p]+2;
		for(p=f[p];S[m-mx[p]-1]!=S[m];p=f[p]);
		f[np]=s[p][c]; lst=s[lst][c]=np;
	} else lst=s[p][c]; ++sz[lst];
}
int main(){
	scanf("%s",S+1); n=strlen(S+1);
	for(int i=1;i<=n;++i) extend(S[i]-'a'),g[i]=mx[lst];
	memset(s,0,sizeof s); cnt=1; lst=0; m=0; reverse(S+1,S+1+n);
	for(int i=1;i<=n;++i) extend(S[i]-'a'),ans=max(ans,mx[lst]+g[n-i]);
	printf("%d\n",ans);
}
是的!代码非常简短,比SAM少了一半!

让后上面那个APIO2014的题目也可以这样做了

CODE:

#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 300010
#define LL long long 
using namespace std;
char S[N]; LL ans=0;
int n,m=0,cnt=1,lst=0;
int s[N][26],f[N]={1},sz[N],mx[N]={0,-1};
inline int extend(int c){
	int p=lst,np; 
	for(++m;S[m-mx[p]-1]!=S[m];p=f[p]);
	if(!s[lst=p][c]){
		np=++cnt; mx[np]=mx[p]+2;
		for(p=f[p];S[m-mx[p]-1]!=S[m];p=f[p]);
		f[np]=s[p][c]; lst=s[lst][c]=np;
	} else lst=s[p][c]; ++sz[lst];
}
int main(){
	freopen("palindrome.in","r",stdin);
	freopen("palindrome.out","w",stdout);
	scanf("%s",S+1); n=strlen(S+1);
	for(int i=1;i<=n;++i) extend(S[i]-'a');
	for(int i=cnt;i;--i){
		sz[f[i]]+=sz[i];
		ans=max(ans,(LL)sz[i]*mx[i]);
	}
	printf("%lld\n",ans);
}
ok,剩下的,后缀平衡树,最后一个啦
后缀平衡树 Suffix Balanced Tree

应该是所有后缀数据结构中最强大的吧,但是其实非常好理解

简单的话,一句话就可以概括:用平衡树动态维护后缀数组,支持在开头插入或者删除

当然怎么正确实现就是非常讲究的了

先来讲一个非常好的实现方法

我们上文提到了用哈希比较两个字符串让后排序构建后缀数组的方法

在这里,我们依然可以沿用这个方法,比如我们可以配合STL让后非常巧妙地完成一些简单的题目

你有一个字符串S,开始为空,现在有两种操作: 
1. 在S后面加入一个字符c 
2. 删除S最后一个字符(保证进行该操作时S不为空) 
每次操作后输出当前S中有多少个不同的连续子串。

这个如果没有插入和删除就是非常简单的SA模板题,答案就是Σi-height[i]

让后考虑加上插入的情况,因为set可以支持求前继和后续,所以可以用哈希实现快速维护height数组

让后因为是在末尾插入,而后缀平衡树不支持在结尾插入,所以要将整个串反过来

code点这里

好了,现在我们发现一个问题:比如有人将这道题数据范围改成了50W

而50W的话,我们用哈希实现的后缀平衡树是O(n lg^2 n)的,显然就卡不过了

现在考虑怎么优化

我们发现,插入一个后缀,比较的无非就是第一个字符,或者是已经在树中的两个后缀

如果我们能做到O(1)比较,就可以避免浪费时间

所以问题变成了这样:如何做到在O(1)的时间内比较两个节点的先后顺序?

一个非常好的方法就是:我们给每个节点一个排名值,是一个实数,每次插入之后,新的节点的排名值为前继+后续的排名值/2

这样,每次比较排名值,就可以快速得到答案

但是这样是有巨大问题的,精度不够!

我们可以按顺序插入,如果这样,每个节点的排名值会非常小,很快就精度爆炸

所以我们需要利用平衡树的性质,每个节点的排名值变为一个区间[l,r],而且必须严格维护,这样精度就不会爆炸

但是就有另一个问题了,平衡树需要旋转,而一次旋转会导致整颗子树的排名值改变,最坏情况就是O(n)的

所以我们需要用到一类叫做重量平衡树的平衡树,这个东西的准确定义是,每次旋转,收到影响的子树,大小是严格/期望/均摊O(lg n)的(这个东西保证了复杂度不会太坏),或者这种结构本身并不需要旋转来保持平衡

重量平衡树常用的有以下几种:
旋转类:

1*.Treap

2.红黑树(极不推荐因为我也只是听说他是重量平衡的,OI没什么人打)

Treap可谓平衡树中的神器,好写,常数小,支持分裂合并,还可以可持久化

关于treap的重量平衡:

当一个节点x旋转到k的位置后,发生重构的代价为O(size[k])

但是x可以旋转到k的概率是O(1/size[k]),所以期望代价是O(1)

非旋转类:

1.跳表

2*.替罪羊树

第二个东西非常重要,因为这个东西应该是所有平衡树里面速度最快的(我也不知道为什么)

而且非常适合用来维护一些奇奇怪怪的信息,因为它用重构来保持平衡,维护信息完全不需要任何额外代价,其效率是其他任何平衡树都不可以比的

当然这个东西不好写,所以一般都写treap,实在不行再用它来卡时间

关于上面那道题,用正版的后缀平衡树写的,确实比我的山寨版快,大概只要1/3的时间(才快3倍还是set给力啊)


好了,所有内容到这里就讲完了,完结撒花

大家可以随便翻翻后面几篇文章,大都是字符串的例题

有什么问题欢迎指出,可以在下方评论区参与讨论!

                                                                                               ——————扩展の灰

python实战应用讲解-【numpy专题篇】字符串函数(附python示例代码) NumPy 字符串函数,字符数组类(numpy.char)中定义了一些函数,这些函数用于对 dtype 为 numpy.string_ 或 numpy.unicode_ 的数组执行向量化字符串操作,它们基于 Python 内置库中的标准字符串函数。 阅读详情

相关推荐

Excel 中使用 VBA 自定义函数(及示例下载)

本文介绍了Excel VBA中自定义函数的开发与使用方法。内容涵盖:1)基础概念,区分Function和Sub的语法;2)实际案例展示如何开发用于航班信息提取的自定义函数(日期、编号、时间提取);3)详细讲解参数验证算法、缺失检测和比例计算逻辑;4)提供错误处理、性能优化等实践建议。文章通过具体代码示例演示了函数的实现与调用方式,并解答常见问题。这些自定义函数可有效提升Excel数据处理效率,特别适合处理结构化文本信息。

小猫三两只。 1910

[置顶] 字符串专题讲解

最近教练叫我去讲字符串专题,于是来写一写这方面的内容 主要就讲以下几个吧: 1.Kmp 2.Extended Kmp 3.Trie 4*.AC Automation (Trie Graph) 5*.String Hash 6.Suffix Array 7*.Suffix Automation 8*.Suffix Tree 9.manacher 10*.PalindromeAuto...

dianning8393的博客 388

excel中161个VBA_自定义函数超级实用

目录 '1.函数作用:返回 Column 英文字........................9 '2.函数作用:查询某一值第num次出现的值................9 '3.函数作用:返回当个人工资薪金所得为2000元(起征点为850元)时的应纳个人所得税税额.............................10 '4.函数作用:从形如"123545ABCDE"的字符串中取出数字....11 '5.函数作用:从形如"ABCD12455EDF"的字符串中取出数字...11 '6.函数作用:按SplitType取得RangeName串值中的起始位置12 '7.函数作用:将金额数字转成中文大写....................13 '8.函数作用:计算某种税金..............................18 '9.函数作用:人民币大、小写转换........................19 '10.函数作用:查汉字区位码.............................20 '11.函数作用:把公元年转为农历.........................21 '12.函数作用:返回指定列数的列标.......................42 '13.函数作用:用指定字符替换某字符.....................43 '14.函数作用:从右边开始查找指定字符在字符串中的位置...43 '15.函数作用:从右边开始查找指定字符在字符串中的位置...44 '16.函数作用:计算工龄.................................44 '17.函数作用:计算日期差,除去星期六、星期日...........45 '18.函数作用:将英文字反转的自定函数...................46 '19.函数作用:计算个人所得税...........................46 '20.函数作用:一个能计算是否有重复单元的函数...........47 '21.数字金额转中文大写................................48 '22.函数作用:将数字转成英文...........................49 '23.函数作用:人民币大小写转换.........................52 '24.函数作用:获取区域颜色值...........................53 '25.函数作用:获取活动工作表名.........................53 '26.函数作用:获取最后一行行数.........................54 '27.函数作用:判断是否连接在线.........................54 '28.函数作用:币种转换.................................54 '29.函数作用:检验工作表是否有可打印内容...............55 '30.函数作用:查找一字符串(withinstr)在另一字符串中(findstr1)中某一次(startnum)出现时的位置,返回零表示没找到。..................................................57 '31.函数作用:增加文件路径最后的“\”符号..............58 '32.函数作用:计算所得税...............................58 '33.函数作用:从工作表第一行的标题文字以数字形式返回所在列号..................................................58 '34.函数作用:在多个工作表中查找一个范围内符合某个指定条件的项目对应指定范围加总求和..........................59 '35.函数作用:返回 Column 英文字.......................60 '36.函数作用:查找指定列名的列数.......................60 '37.函数作用:文字格式的时间(分:秒)转化为数字格式(秒)..61 '38.函数作用:将"hh:mm:ss"格式的时分秒数转换成秒数.....62 '39.函数作用:金额中文大写转数字.......................62 '40.函数作用:把角度转为度秒分、弧度等显示.............63 '41.函数作用:身份证号码侦测...........................64 '42.函数作用:显示公式.................................65 '43.函数作用:方便财务人员理帐查找.....................66 '44.函数作用:数值转换为字符地址.......................68 '45.函数作用:字符地址转换为数值.......................69 '46.函数作用:等待时间(以秒计算).....................69 '47.函数作用:得到字符串实际的长度(以单字节记).......70 '48.函数作用:18位身份证最后一位有效性验证............70 '49.函数作用:计算符合maturity condition的拆解金额....72 '50.函数作用:对多个用同一分隔符分隔的待查找元素,逐一在表区域首列内搜索,将返回选定单元格的值相加,............72 '51.函数作用:根据个人所得税(工资)反算工资数.........73 '52.函数作用:判断表是否存在...........................74 '53.函数作用:角度转弧.................................74 '54.函数作用:比较相同的字符串.........................75 '55.函数作用:对选定的数组进行排序.....................76 '56.函数作用:取得指定月份天数.........................77 '57.函数作用:排序工作表活页薄.........................77 '58.函数作用:统计数组中非重复数据个数.................78 '59.函数作用:摘取子字符串.............................79 '60.函数作用:计算20000余个汉字的笔画.................79 '61.函数作用:删除当前工作表中的全部超连接.............80 '62.函数作用:取得相近数据.............................81 '63.函数作用:提取定串中汉字...........................81 '64.函数作用:搜索重复数据(选定范围)...................81 '65.函数作用:字符型转数字型...........................82 '66.函数作用:小写人民币转大写人民币...................83 '67.函数作用:取得指定月份人星期天个数.................84 '68.函数作用:侦测档案是否包含宏.......................84 '69.函数作用:获取循环参照单元格.......................85 '70.函数作用:创建桌面快捷方式.........................86 '71.函数作用:自动建立多级目录.........................86 '72.函数作用:统计经筛选后符合条件的记录条数...........87 '73.函数作用:复制单元格列高与栏宽.....................87 '74.函数作用:取消隐藏工作表(包括vba Project工程保护的)88 '75.函数作用:删除单元格自定义名称.....................88 '76.函数作用:从文件路径中取得文件名...................89 '77.函数作用:取得一个文件的扩展名.....................89 '78.函数作用:取得一个文件的路径.......................90 '79.函数作用:十进制转二进制...........................90 '80.函数作用:检查一个数组是否为空.....................90 '81.函数作用:字母栏名转数字栏名.......................91 '82.函数作用:数字栏名转文字栏名.......................91 '83.函数作用:判断一件活页夹中是否还有子目录...........92 '84.函数作用:判断一个文件是否在使用中.................92 '85.函数作用:列出档案详细摘要信息.....................93 '86.函数作用:获取菜单ID编号及名称列表................93 '87.函数作用:状态列动态显示文字.......................94 '88.函数作用:取得一个文件的路径2.....................94 '89.函数作用:取得一个文件的路径3.....................95 '90.函数作用:取得Activecell的栏名....................95 '91.函数作用:取得单元格中指定字符前的字符.............95 '92.函数作用:前单元格指定字符前的字符颜色改成红色.....95 '93.函数作用:根据数字返回对应的字母列号...............96 '94.函数作用:取工作表名字.............................96 '95.函数作用:取消所有隐藏的宏表.......................97 '96.函数作用:导出VBA Project代码.....................97 '97.函数作用:导入VBA Project代码.....................97 '98.函数作用:取得汉字拼音的第一个字母.................98 '99.函数作用:获取两栏中相同的数据....................100 '100.函数作用:选取当前工作表中公式出错的单元格﹐关返回出错个数...............................................101 '101.函数作用:将工作表中最后一列作为页脚打印在每一面页尾101 '102.函数作用:获取vbproject引用项目.................102 '103.函数作用:移除Excel工作表中的外部数据连接.......103 '104.函数作用:将选择定单元格作成镜像图片.............103 '105.函数作用:反选择单元格中的数.....................105 '106.函数作用:在Excel中加入一个量度尺(以厘米为单位).106 '107.函数作用:在Excel中加入一个量度尺(以寸为单位)...109 '108.函数作用:取得一个短文件名的长文件名.............111 '109.函数作用:取得临时文件名.........................112 '110.函数作用:等用Shell调用的程序执行完成后再执行其它程序...................................................112 '111.函数作用:将Mouse显示成动画.....................113 '112.函数作用:限制Mouse移动范围.....................114 '113.函数作用:取得当前激活窗品句柄及标题.............114 '114.函数作用:取得屏幕分辨率.........................115 '115.函数作用:自动建立多级目录.......................115 '116.函数作用:将文件长度置零.........................116 '117.函数作用:读取WIN9X / Me共享文件夹密码..........116 '118.函数作用:取得预设的打印机及设置预设的打印机.....119 '119.函数作用:获得当前操作系统的打印机个数及检测打印是否存在.................................................120 '120.函数作用:枚举打印机名称清单.....................120 '121.函数作用:读取网络服务器当前时间.................122 '122.函数作用:下载文件到指定目录.....................123 '123.函数作用:自动映射网络驱动器.....................124 '124.函数作用:自动断开网络驱动器.....................125 '125.函数作用:连接选定单元格中的内容.................125 '126.函数作用:获取一个单元格中有指定字体颜色部份数据.126 '127.函数作用:对指定文件加XLS加密...................126 '128.函数作用:选择指定范围内使用了填充颜色的单元格...127 '129.函数作用:在特定的区域内查找文本,返回值是包含查找文本的单元格...........................................127 '130.函数作用:返回特定区域中最大值的地址.............128 '131.函数作用:删除表格中使用范围内的所有空白单元格...129 '132.函数作用:返回数组中有多少个指定的字符串.........129 '133.函数作用:返回当前工作表中引用了指定的单元的地址.130 '134.函数作用:获取Excel中字型列表...................131 '135.函数作用:获取一个字符串中有多少个数字字符.......131 '136.函数作用:在Excel中对多列进行填充...............131 '137.函数作用:对选定的范围进行数据填充(忽略单元格格式)132 '138.函数作用:VBA Project加密及解密.................132 '139.函数作用:列出收藏夹中的网址.....................133 '140.函数作用:计算两个日期之间相隔的年份,比如年龄,工龄等.可计算从1000年01月01日起的日期....................134 '141.函数作用:从字符串提取纯数字.....................135 '142.函数作用:将一个数组按升序排列...................136 '143.函数作用:将一个数组按降序排列...................137 '144.函数作用:删除空白列.............................137 '145.函数作用:判断工作表是否为空白...................138 '146.函数作用:将数据按类分到不同工作薄...............138 '147.函数作用:单元格内数据排序.......................139 '148.函数作用:对多栏排序.............................140 '149.函数作用:返回计算公式的值 [,值的计算公式].......140 '150.函数作用:把第一列=某个值对应的第二列的内容连在一起,并用、隔开...........................................141 '151.函数作用:取得系统使用模式.......................142 '152.函数作用:计算机注销/关机/重启...................142 '153.函数作用:更改计算机名称.........................143 '154.函数作用:从n位开始取出字符串中的汉字、英文字母、数字...................................................143 '155.函数作用:在指定列中寻找含有指定字符串的单元格,并将符合条件的单元格标为红色,并将对应的下一列单元格赋值为1。.....................................................144 '156.函数作用:清除字符串中的空格.....................145 '157.函数作用:查找合并单元格位置.....................145 '158.函数作用:阴阳历转换和阴阳历生日.................145 '159.函数作用:利用数组和Substitute来替换某字符......149 '160.函数作用:一键创建斜线表头.......................150 '161.函数作用:自动获取指定月的工作日.................151

VBA常用函数和举例

VBA(Visual Basic for Applications)是Microsoft Office应用程序(如Excel、Word等)中用于自动化任务的一种编程语言。在VBA中,有许多常用的内置函数,这些函数可以帮助开发者高效地处理数据、字符串、日期时间等。

weitingfu的博客 2703

Excel VBA自定义常用函数

包含8个自定义函数: 1.提取中文,支持多个中英文交替2.赋值3.Copy首行,给其他行赋值4.获得链接5.截字符串中首尾两个-之间的部分6.提取中文前面的英文7.截取字符串中最后一次出现要str之后的字符串

经典常用的VBA实例,自定义函数,系统函数

绝对物有所值哈,经典常用的VBA实例,自定义函数,系统函数

Java 字符串专题讲解(整合)

1、判断字符串有无重复字符 2、翻转字符串 将字符串逆序 3、变形词:一个字符串重新排序能否变为另外一个字符串 4、替换字符串的空格 5、压缩字符串 6、判断两个字符串的字符集是否相同 7、判断一个字符串循环位移后是否包含另外一个字符串 8.将字符串按单词翻转 9、去掉字符串中连续出现K次的0 10、回文串的判定 11、生成最短摘要 尺取法 12、字符串匹配 KMP算法 13、hiho字符串 ...

覃会程的博客 5210

Fluent 中多相流模型全面解析

3.颗粒或液滴在连续相中的运动:若关注颗粒或液滴在连续相中的运动轨迹、分布规律以及与连续相的相互作用,如喷雾干燥、气力输送等过程,Eulerian-Lagrangian 模型则是最佳选择。Eulerian-Eulerian 模型可以准确模拟气体在固体颗粒中的流动情况,包括颗粒的流化状态、气体与颗粒之间的传热传质过程,为气固流化床的优化设计和操作提供依据。通过该模型,可以了解混合器内不同位置的混合程度、颗粒的运动轨迹以及液固相间的相互作用,从而改进混合器的结构和操作条件,提高混合效率。

khhdp的博客 3085

java字符串专题讲解_java字符串全面解析

String S1 = “This is only a" + “ simple" + “ test";StringBuffer Sb = new StringBuilder(“This is only a").append(“ simple").append(“ test");你会很惊讶的发现,生成 String S1 对象的速度简直太快了,而这个时候 StringBuffer 居然速度上根本一点...

weixin_39765325的博客 130

硬件基础(4):(7)ADC电压采集:软件校准(标定、拟合)

在实际项目中,**软件端校准**可以有效弥补硬件方面(分压电阻误差、放大器失调、参考源误差等)带来的系统性偏差,从而提高 ADC 测量精度。下面是一个常见的**多点实测校准**流程与思路,个人感觉传感器标定的原理也只这样。

FCZlll的博客 6240

基于SHIS凝视型短波红外高光谱相机(900-1700nm)区分不同宝石的差异性和可分性(宝石分选)

SHIS系列凝视型高光谱相机基于液晶可调谐滤波器(LCTF),通过电控LCTF进行连续细分窄波段分光,对每个波段进行高分辨率的二维面阵成像,进行图像处理后,获得完整的高光谱高清图像序列。具有低功耗、小型化、使用方便和易于扩展的优势。其工作波段覆盖400nm至1000nm,能够捕捉宝石在不同波长下的光谱特征,为宝石鉴定提供了新的工具。

wayho的博客 707

权重值-字符串-10春机考-专题讲解

权重值-字符串-10春机考-专题讲解 1、计算字符串s中每个字符的权重值,所谓权重值就是字符在字符串中的位置值与该字符 的ASCII码值的乘积。位置值从1开始依次递增。将每个字符的权重值以格式“%d”写入到 指定文件。 #include #include void main() {     FILE*p;  int i,w;     char*s="we45*&y3r#$1";

Java技术博文 1885

【Transformer 相关理论深入理解】注意力机制、自注意力机制、多头注意力机制、位置编码

transformer原理

qq_38253797的博客 2万+

删除字符(字符串&数学专题讲解)

小可讨厌单词mapmap,达达讨厌单词piepie。现在给你一个长度为nn的字符串,请问最少删除多少个字符,使得mapmap和piepie都不在这个字符串中出现。对于每组输入,输出一行一个整数,表示最少删除多少个字符,使得map和pie都不在这个字符串中出现。对于每组输入,第一行一个正整数n(1≤n≤10​5​​),表示字符串的长度。第一行给定一个整数T(1≤T≤10​4​​),表示有T组输入。保证TT组输入的字符串长度的总和不超过10^5​​。第二行一个长度为n的字符串,只包含小写字母。

DZ19476的博客 211

从C-EPS到DP-EPS:手把手图解不同EPS类型怎么选,对你的车操控感影响有多大?

方向盘转动时那股细腻的阻尼感,高速过弯时令人安心的沉稳,或是狭窄车位里一根手指就能拨动的轻盈——这些看似微妙的差异,其实都源自转向系统电机那几厘米的安装位置变化。当我们谈论驾驶乐趣时,发动机参数和悬架调校总是占据C位,但真正连接驾驶者与车辆的神经末梢,恰恰是那套默默工作的电动助力转向系统(EPS)。不同EPS类型带来的操控特性差异,可能比马力数字更能定义一辆车的性格。

weixin_30920513的博客 531

卷积码Viterbi译码算法原理及C语言实现

具体而言,卷积码的编码过程是将输入数据和编码器中的状态进行异或操作,输出码字为各个状态对应的结果。Viterbi译码算法是一种最大似然译码算法,根据接收到的含有噪声的码字,寻找出最有可能的原始数据序列。(2) 递推计算:根据接收到的码字和当前状态,计算下一时刻各个状态的路径度量,并记录每个状态的前驱状态。以上代码实现了一个简单的卷积码Viterbi译码算法,通过接收到的码字进行译码,并输出解码结果。(1) 初始化:将所有状态的路径度量初始化为一个很大的值,选择路径度量最小的状态作为起始状态。

2301_79331421的博客 825

完美序列 字符串&数学专题讲解

第二行n个正整数a​1​​,a​2​​,⋯,a​n​​(1≤a​1​​<a​2​​<⋯<a​n​​≤10​9​​),表示序列a一开始的元素。每次操作,小可选定序列中的两个数字xx和yy,满足x > y并且x-y不在序列a中。一开始这个序列aa有nn个元素,分别是a​1​​,a​2​​,⋯,a​n​​。对于每组输入,第一行一个正整数n(2≤n≤10​5​​),表示序列a一开始的长度。对于每组输入,输出一行一个整数,表示序列变成完美序列之后,长度最长是多少。样例中,第一组输入本身就是完美序列,无法进行操作。

DZ19476的博客 298

如何正确的计算两个角度的角度差?

这一步是必要的,因为它确保了无论a相对于b是在逆时针方向还是顺时针方向,d2总是正确地反映出绕圆相反方向的角度差。这样,当函数进行最后的比较时,它能够准确地比较两个方向的旋转路径并选择最短的一个。

小秋 AI SLAM 入门实战教程 4135

双质数 字符串&数学专题讲解

如果一个数字除了1和它自身以外,只有一个正整数因数,那么这个数字就被称为“双质数”。第一行一个正整数n(1≤n≤10​5​​),代表要询问nn个数字。接下来nn个正整数字,每个数字的大小在[1,10^12]范围内。现在给出nn个数,请你判断每个数字是否是双质数。对于询问的每个数字,如果是双质数,输出。

DZ19476的博客 326

OpenClaw 多机器人多 Agent 模式:打造你的 AI 助手团队

OpenClaw多Agent模式为企业提供高效的AI助手团队解决方案。通过创建4个独立的飞书机器人(主助理、内容创作、技术开发、AI资讯),每个连接专属Gateway和Agent模型(如Claude Opus/Sonnet、Gemini 2.5),实现专业分工。相比单Agent模式,多Gateway架构具有配置清晰、互不干扰、无需手动切换等优势。系统采用Profile隔离机制,各Agent拥有独立配置和端口。部署过程包括创建机器人应用、配置Agent、运行自动化脚本等步骤,支持私聊或群组@使用。日常提供状态

Maynor的博客 5096

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

java字符串专题讲解_java String对象解析(很经典)

java String对象解析(很经典)1. 首先String不属于8种基本数据类型,String是一个对象。因为对象的默认值是null,所以String的默认值也是null;但它又是一种特殊的对象,有其它对象没有的一些特性。2. new String()和new String(“”)都是申明一个新的空字符串,是空串不是null;3. String str=”kvill”;String str=n...

weixin_29128423的博客 118

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

因数分解 字符串&数学专题讲解

对于每组输入,输入一个正整数n(2≤n≤10​^9​​),含义如题所示。如果有多解,保证a最小,在a最小的前提下保证b最小。的大于等于22的整数a,b,ca,b,c,保证a<b<c。第一行一个正整数T(1≤T≤100),表示有TT组输入。给定一个正整数nn,请你把nn分解成三个。对于每个n,如果不能按照要求分解,输出。,然后换行输出分解出来的三个正整数。

DZ19476的博客 220

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

python字符串前面去两位_2500字 字符串专题总结

我的完整施工计划已完成专题:1.我的施工计划2.数值专题这篇是字符串专题总结除了常见的数值型,字符串是另一种常遇到的类型。一般使用一对单引号或一对双引号表示一个字符串。字符串中如果遇到 \ 字符,可能是在做字符转义,所谓的转义便是字符的含义发生改变,比如常用的\n 组合,转义后不再表示字符n本身,而是完成换行的功能。类似的,还有很多转义字符,如 \t, 正则表达式中 \s,\d等等。接下...

weixin_31854817的博客 346

【算法笔记】有人看海,有人被爱,有人做不出 leetcode 第一题(字符串专题)

反转字符串、反转字符串Il、卡码网:替换数字、翻转字符串里的单词、卡码网:55.右旋转字符串、实现strStr、双指针回顾、重复的子字符串、字符串总结、KMP

OuO-1 的博客 426
上一篇: Jzoj3467 最长上升子序列
下一篇: HDU2896 病毒侵袭
扩展的灰
博客等级 码龄12年 60粉丝 342原创
评论 10
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值