大家好,我是黄人和,一只小菜鸡。本文长期更新,记得收藏点赞关注三连

前几日王烨程问我KMP的缘故,今天中午他又在问我这个。之所以很久没有回答他,一来是近来事务繁忙,未尝闲暇,二来是我也对字符串算法有点生疏。听说他开了个公众号,且今日终于有一点时间,开更

我肯定会写得非常详细,按照以下顺序来介绍:

我在写完知识点后,会放一些题目,配上讲解。前面的自然很简单,给大家入门学习的,后面的难度有点少儿不宜,真都学懂是很难非常难的噢~

字符串基础

信知生男恶,反是生女好
生女犹得嫁比邻,生男埋没随百草

什么是字符串呢?字符串是一个string(废话)。字符串是一个字符数组,从0开始编号,里面填的都是ASCII字符。 下标1不是第一个字符,因为有下标0。

注意:用input跟cin输入的时候,空格和换行会被吃掉。

字符串的几个定义:

子串:字符串连着的一个串串,比如说"12345"的字串有"1234",哈哈。子序列:不需要连着的一部分,中间可以隔着几格,比如说"12345"的一个子序列是"134"。

前缀:英语都学过吧,什么ex啊,pre啊。后缀就是nal、lize、lism这类。其实不完全对,前缀指的是不包括某个元素的前面按顺序排列,后缀同理。举个栗子:bcabcd 的所有后缀为 {d, cd, bcd, abcd, cabcd, bcabcd, abcabcd}

回文:正着读跟反着读是一样的,比如“上海自来水来自海上”就是一个回文串,或者“云南丽江城江丽南云”。

字符串是一个封装的数据结构,有许多内部函数封装在里面,给你各种操作,具体可以看看漫画学Python。Python有append、split、sort、count等等函数,用来实现增添替换等操作。string类型有加和乘的定义,加就是连接字符串,这个加法在Python跟C++都能用,乘就是把字符串变成几倍,比如:abc*2=abcabc。但是笔者不怎么懂Python,这里谈一谈C++的字符串函数,从0开始编号,C用strlen,C++用size或者length取字符串大小。find跟rfind啥的用法都跟Python一样,来看代码。注意find如果找不到会返回正无穷而不是最后一个下标。

然后是字符串的增添和删除,C++跟Python是一样的。下图的输入我忘记截进去了

截取字符串是substr,substr(pos,n)代表从pos开始截取长度为n的字符串。

C语言的字符串函数是另一套,跟Python不能混用。我觉得没有必要在这里写

然后我们看题。你可以跳掉题目,左边有提纲。

我们搜出来所有的"you"然后替换就OK了。代码如下:

#include<iostream>
#include<cstring>
#include<string>
#define RE register int
#define IL inline
#define N 100001
using namespace std;
string s;
int main(){while(getline(cin,s)){RE len=s.size();for (RE i=0;i<len;i++){if (s[i]=='y'&&s[i+1]=='o'&&s[i+2]=='u')cout<<"we",i+=2;else cout<<s[i];}cout<<endl;}return 0;
}

注意:我们在这里使用了getline输入空格。这个题还可以使用s.find,find返回是是第一个位置,我们只需要不断地往后找,就能找出所有的目标子串了,简单吧。

下面这一题是叫你倒着输出,然后没了?不是的,这个题需要注意的点很多。 C++输入的时候第一行的那个空格会被当成一行,所以需要用getchar吃掉那个换行符。Python遇到这个问题我还不知道怎么解决。

代码:

#include<iostream>
#include<cstring>
#include<string>
#define RE register int
#define IL inline
#define N 100001
using namespace std;
string S,s,s1;
int n;
int main(){cin>>n,getchar();while(n--){getline(cin,s),S="",s1="";RE len=s.size();for (RE i=0;i<len;i++){if (s[i]^' ')s1=s[i]+s1;elseS+=s1+' ',s1="";}cout<<S+s1<<endl;}
}

我坐在401,前面有一对couple在秀恩爱,我在吃狗粮。。。唉什么时候能有对象呢。程程上个星期也在问我有没有恋爱的想法,我信誓旦旦地表示要为科学事业奋斗终身,但其实只是个借口,我根本找不到对象。未来还要去相亲.......

哎扯远了,回到题目上来,下一题。

我翻译一下:

定义字符串距离为两个长度相同的字符串的相反位置ASCII值之差求和。就是给了T个字符串跟距离的最大值m,然后求满足距离小等于m的最大字符串长度。数据范围也不大,N^3是很好写的,但是必炸,或许我可以二分一波把复杂度弄到logM*N^2,反正都是TLE的。我就去搜了这个题的解答,果然我是个垃圾。正解是N^2。

来说说这个题怎么搞:

题目说的距离是取相反的位置求ACSII差的和,两个字符串不重合,肯定存在一个最中间点。所以枚举字符串的每个字符跟字符中间作为对称轴,然后构造数组来记录每个相反位置的ACSII差。

该部分代码如下:

cin>>m>>s;RE len=s.size();for (RE cnt,i=0;cnt=0,i<=len;i++){//枚举回文串的中心ifor (RE j=1;j+i<len&&j<=i;j++)//枚举从i拓展的单个距离num[cnt++]=abs(s[i+j]-s[i-j]);ans=max(ans,solve(cnt));}for (RE cnt,i=0;cnt=0,i<=len;i++){//枚举回文串从字符中间断开的情形for (RE j=1;j+i-1<len&&j<=i;j++)//同理构造距离数组 num[cnt++]=abs(s[j+i-1]-s[i-j]);ans=max(ans,solve(cnt));}

solve就是所谓的尺取法了。我也是今天才知道叫这个名字,它是一种用于求解枚举求最大区间的高效方法。我就举个例子,如下:寻找最长的区间就是弄两个指针在跑,每次先动s更改sum,然后动t同时修改sum,所以对于每个区间枚举左侧都能以最快速度找到最右的右侧,最后得到的一定是最大的区间。

校园网就TM烂得要死,Onlinejudge一直submit failed,我直接上代码了。

#include<cmath>
#include<iostream>
#include<cstring>
#include<string>
using namespace std;
#define RE register int
#define IL inline
#define N 5001
string s;
int m,T,ans,a[N];
IL int check(RE len){RE s=0,t=0,ans=0,sum=0;//s和t是左右指针,ans是最大的长度,sum是距离之和for (RE s=0;s<len;s++){//遍历s不断移动while(sum+a[t]<=m&&t<len)//移动t到最右边sum+=a[t],t++;ans=max(t-s+1,ans),sum-=a[s];}return ans;
}main(){  cin>>T;while(T--){cin>>m>>s;RE len=s.size();for (RE cnt,i=0;cnt=0,i<=len;i++){//枚举回文串的中心ifor (RE j=1;j+i<len&&j<=i;j++)//枚举从i拓展的单个距离a[cnt++]=abs(s[i+j]-s[i-j]);ans=max(ans,check(cnt));}for (RE cnt,i=0;cnt=0,i<=len;i++){//枚举回文串从字符中间断开的情形for (RE j=1;j+i-1<len&&j<=i;j++)//同理构造距离数组a[cnt++]=abs(s[j+i-1]-s[i-j]);ans=max(ans,check(cnt));}cout<<ans<<endl;}return 0;
} 

后面的一些入门题我就不想放上来了,第一是浪费版面我不爱写,第二你们也不要读。

问题:假如you are given一个计算式,包括加减乘除和小括号,比如1+(3*2+2)/5*4-2,我知道你用笔肯定会算。请问你要怎么设计程序让电脑也会算呢?如果我再加上乘方和中括号,你还会算吗? 我用笔也有可能把运算顺序给搞错。这就是个大模拟,难度不大但是要写500行以上的代码,这个题又很催眠,我怕写了就睡着了。。。

哈希

伤心桥下春波绿,曾是惊鸿照影来

终于写到了这里,hash函数是一个很有意思的东西咩。我不会给你介绍原理,直接告诉你是怎么实现的。

问题背景:我们遇到了这样的一个问题,给定大字符串S长度为1000000,小字符串s长度1000,请问如何寻找S中子串s的位置?你如果是一个愚夫,你肯定要这样做:直接枚举S的每个字符当作起点,然后与s进行比较。这样做必然超时,因为达到了1000000000次计算。或者你用find函数来查找,照样是超时的。所以我们想出了一个方法:将S的所有子串和s分别哈希加密,然后比较哈希值,如果相同就有很大的概率(99%以上)相同。

hash cryption的过程是这样的:

上面的X是一个质数(我设定为163),n是字符串的长度哈,显然,对于S的每个子串都有各自的哈希值,这样生成的哈希值很大概率不一样。

假设hash[i]表示前i项的hash值。这样,我们得到:

所以我们把求这个多项式的N^2降维到N了。我们通过线性递推,得到前i个字符的hash值即可。

然后有一个很好玩的操作。从i到j的字符串哈希值可以由从1到j和从1到i的哈希值算出来!

这个公式的推导如下:

然后是自写模板。

#include<bits/stdc++.h>
using namespace std;
#define RE register int
#define IL inline
#define int unsigned long long
#define N 10001
string s1,s;
int x=163,t,hash[N];
main(){cin>>s>>s1;RE len=s.size(),len1=s1.size(),p=pow(x,len1);for (RE i=1;i<=len;i++)    hash[i]=x*hash[i-1]+s[i-1]-'a';for (RE i=0;i<len1;i++)    t=x*t+s1[i]-'a';for (RE i=0;i+len1<=len;i++)if (hash[i+len1]-hash[i]*p==t)cout<<i<<' ';
}

说完了模板,水题做了一些但我不要放上来,哈哈。让我们快进到字典树。

字典树

玲珑骰子安红豆,入骨相思知不知

字典树和KMP是AC自动机的基础,字符串相对来说理解是很简单的,KMP则比较阴间。顾名思义,字典树可以添加单词到字典里面,然后查找是否有单词在字典中。

下图是一个字典树的例子。

根节点假设为0号节点,waterwishwintietired等等单词都可以这样储存。

在建树的时候,用trie[p][c]表示p节点的后继,如果没有后继就新建一个节点,用cnt记录每个节点。最后用一个vis记录p是否为叶节点。代码如下:

IL void insert(string s){RE p=0;for (RE i=0;s[i];i++){RE c=s[i]-'a';if (!trie[p][c])  trie[p][c]=++cnt;p=trie[p][c];}vis[p]=1;
}

查询是一样的,通过p=trie[p][c]可以遍历可能的节点。直接放代码:

IL bool find(string s){RE p=0;for (RE i=0;s[i];i++){RE c=s[i]-'a';if (!trie[p][c])    return 0;p=trie[p][c];}return vis[p];
}

解释为什么需要return vis[p]?因为假如你需要查找internation是否在字典里面。但是字典里面只有international,所以需要看看是不是存在以n为结尾的单词internation。

我们发现其实每个节点都只是用了一次,剩下的空间都浪费了,没错就是这样子。用空间换时间,来看模板:

#include<bits/stdc++.h>
using namespace std;
#define RE register int
#define N 1000001
#define IL inline
int trie[N][26],cnt;
bool vis[N];
IL void insert(string s){RE p=0;for (RE i=0;s[i];i++){RE c=s[i]-'a';if (!trie[p][c]) trie[p][c]=++cnt;p=trie[p][c];}vis[p]=1;
}IL bool find(string s){RE p=0;for (RE i=0;s[i];i++){RE c=s[i]-'a';if (!trie[p][c])  return 0;p=trie[p][c];}return vis[p];
}
int m,n;
string s;
main(){cin>>n>>m;for (RE i=1;i<=n;i++)cin>>s,insert(s);for (RE i=1;i<=m;i++)cin>>s,cout<<find(s)<<endl;
}

我先放一个水题:

很明显的模板题,我一发就A。如下:

#include<bits/stdc++.h>
#define RE register int
#define IL inline
#define N 500001
using namespace std;
int trie[N][26],vis[N*26],cnt;
int n,m;
IL void insert(string s){RE p=0,c;for (RE i=0;s[i];i++){c=s[i]-'a';if (!trie[p][c])  trie[p][c]=++cnt;p=trie[p][c];}vis[p]=1;
}IL string find(string s){RE p=0,c;for (RE i=0;s[i];i++){c=s[i]-'a';if (!trie[p][c]) return "WRONG";p=trie[p][c];}if (!vis[p])    return "WRONG";if (vis[p]&1){vis[p]=2;return "OK";}if (vis[p]==2)    return "REPEAT";
}
int main(){string s;cin>>n;for (RE i=0;i^n;i++)    cin>>s,insert(s);cin>>m;for (RE i=0;i^m;i++) cin>>s,cout<<find(s)<<endl;return 0;
}

本来这里有一个树状数组套trie的题,还是不要放在这里的好,被我删了。

还君明珠双泪垂,恨不相逢未嫁时

我正在看朋友圈里大家的匿名提问箱,小伙伴们都有好多话可聊,真的好有意思好可爱,我从来不发朋友圈,也不太会和大家说话,只能孤独终老罢了。

卧槽浪费了20分钟刷朋友圈,来讲01字典树。来看模板题:

假如你不会线性基,要怎么做?会线性基也做不了写个暴力必炸,而01字典树能很好的解决这个题目。建树如下,跟字典树是一样的。

显然要找跟已经数异或值最大,肯定是从高位到低位都取跟原来的不一样就行了。来看模板吧

#include<bits/stdc++.h>
using namespace std;
#define RE register unsigned long long
#define int long long
#define IL inline
#define N 3200000
int trie[N][2],cnt,vis[N];
int T,n,m;
IL void insert(RE x){RE p=0,c;for (RE i=31;~i;i--){c=x>>i&1;if (!trie[p][c])   trie[p][c]=++cnt;p=trie[p][c];}vis[p]=n;
}IL int find(RE x){RE p=0,c;for (RE i=31;~i;i--){c=x>>i&1;p=trie[p][(trie[p][c^1])?c^1:c];}return x^vis[p];
}main(){cin>>T;while(T--){cin>>n>>m,cnt=0;memset(trie,0,sizeof trie);memset(vis,0,sizeof vis);for (RE t,i=1;i<=n;i++)cin>>t,insert(t);cout<<"Case #"<<T<<":\n";for (RE i=1,t;i<=m;i++)cin>>t,cout<<find(t)<<endl;}return 0;
}

比如:

你可能不知道题目的npy是啥,就是我很想找的,你懂得。。。

很明显这个题是考的二进制,那么我们直接弄个暴力,然后T了。

这个题有点难,我弄懂了花了非常久。写在下面了

代码:

#include<bits/stdc++.h>
using namespace std;
#define RE register int
#define IL inline
#define N 10000001
const int B=19;
int n,k,f[N],siz[N],dep[N],trie[N][2],cnt=1;
IL void insert(int x){RE p=1,c;for (RE i=B;~i;--i){c=x>>i&1;if(!trie[p][c])    trie[p][c]=++cnt;p=trie[p][c];}siz[p]++;
}IL void dfs(RE u){if(!trie[u][0]&&!trie[u][1])return void(f[u]=siz[u]);if(trie[u][0])dep[trie[u][0]]=dep[u]-1,dfs(trie[u][0]),siz[u]+=siz[trie[u][0]];if(trie[u][1])dep[trie[u][1]]=dep[u]-1,dfs(trie[u][1]),siz[u]+=siz[trie[u][1]];if(k>>dep[u]&1)f[u]=max(f[trie[u][0]]+siz[trie[u][1]],f[trie[u][1]]+siz[trie[u][0]]);elsef[u]=max(f[trie[u][0]],f[trie[u][1]]);
}int main(){cin>>n>>k,cnt=1;for (RE i=1,t;i<=n;++i) cin>>t,insert(t);dep[1]=B,dfs(1),cout<<f[1]<<endl;return 0;
}

另一道模板题,可惜是紫色的:

单源最短路和多源最短路都可以用迪杰斯特拉算法。如果遇到了负权边,你就用Johnson算法把负边去掉就OJBK了,因为SPFA会被卡。

然后这个题要你求的异或,怎么说?有个很有意思的结论:(t^i)^(t^j)=(i^j),所以查询两个点的异或距离,只需处理所有点到第一个点的异或距离就可以了。这又是一棵树,所以按照这个方法,每个点只会出现在一条边只需要扫描一次即可。如果这是一个图怎么算?dijkstra跑个最短路就可以

然后构造一个01字典树,题目要我们求的最长路径,就转化为(1^i)^(1^j)最大。枚举每一条边作为固定的,再通过贪心不断地挑选相反的边按顺序遍历,这样异或出来就是最大的了。复杂度?预处理和建树O(N),贪心O(NlogN)。上代码:


#include<bits/stdc++.h>
using namespace std;
#define RE register int
#define IL inline
#define N 100001
int n,head[N],cnt;
struct edge{int to,next,w;
}e[N<<1];
int dis[N],trie[2000001][2],cnt2,ans;
IL void add(RE u,RE v,RE w){e[++cnt].w=w;e[cnt].to=v;e[cnt].next=head[u];head[u]=cnt;
}IL void dfs(RE u,RE fa){for (RE i=head[u];i;i=e[i].next){RE to=e[i].to,w=e[i].w;if (to!=fa)   dis[to]=dis[u]^w,dfs(to,u);}
}IL void insert(RE x){RE p=0,c;for (RE i=30;~i;i--){c=x>>i&1;if (!trie[p][c])  trie[p][c]=++cnt2;p=trie[p][c];}
}IL int find(RE x){RE p=0,c,ans=0;for (RE i=30;~i;i--){c=x>>i&1;if (trie[p][c^1]) ans+=1<<i,p=trie[p][c^1];else p=trie[p][c];}return ans;
}
int main(){cin>>n;for (RE u,v,w,i=1;i<n;i++)cin>>u>>v>>w,add(u,v,w),add(v,u,w);dfs(1,0);for (RE i=1;i<=n;i++)  insert(dis[i]);for (RE i=1;i<=n;i++) ans=max(ans,find(dis[i]));cout<<ans<<endl;return 0;
}

现在我提出一个问题:假如我要你求严格次小的异或路径,怎么算?我在上一篇文章里面写过类似的题目了。01字典树建树一样的过程,倍增或者树链剖分求LCA,同时维护两个异或值。然后查询每两个点的异或值即可。这个题应该有动态树套trie树的解法,我不会。

01字典树可以跟树上DP和可持久数据结构结合考察,我就不班门弄斧了。

KMP

侯门一入深如海,从此萧郎是路人

我确实有喜欢的女孩子,可是从来不敢profess,害怕被拒绝连朋友都做不了。

受王烨程同学的委托,我经过多次起草和反复斟酌,特此作KMP算法的讲解报告。KMP是一种用于解决模式串匹配问题的算法。简而言之,就是用于查询主串里某些子串的位置。如下图所示:

假如我要写一个这样的算法。我肯定先写暴力瞎几把匹配一波,然后O(N^2)原地猝死,革命事业陷入了严重的危机。在这个前途极度危急的时刻,KMP闪亮登场!!!它用O(N)游刃有余的姿态秒杀此题,创造了新民主主义革命的伟大胜利,让中国人民成功地摆脱了MLE、TLE和WA的三座大山,成功地建立了AC自动机自信、后缀自动机自信、后缀平衡树自信和回文自动机自信的“四个自信”。

所以KMP究竟是怎么实现的呢?它主要分成两步:

第一步:对子串进行自我匹配,构造一个数组next,next数组的含义就是一个固定字符串的前缀和后缀相同的最大长度。前缀和后缀就是一个子串最前面和最后面的部分,但是前缀不能包括最后一个字符,也就是前缀不能是它自己!(后缀同理)

你可能有点懵逼?我举个例子:

a没有前缀,所以next就是0。ab的前后缀是a和b,所以next=0。aba的前后缀最长是a就是1。同理,abab最长前后缀是ab,后面的我不解释了。

上面是另一个例子,自己手算next是不是这样的。我们发现:next[1]=next[2]=0必定成立,此后才能可能出现next!=0

其实next除了前缀和后缀相同的最大长度,其实还有一层含义:next表示的是相同前后缀的前缀的位置,这句话有点拗口,理一下是不是这个样子。比如aaaaa的next等于4,代表最大相同前后缀相同的前缀指针在4位置,也就是第4个a。

下面是求next的代码:

for (RE i=2,j=0;i<=n;i++){while(j&&(a[i]!=a[j+1])) j=next[j];if (a[i]==a[j+1]) j++;next[i]=j;}

这个代码是什么东东?ij分别用来标记前后缀的最大位置,i指针用来表示右边的,j指针表示左边的。为什么从0和2开始?因为它们下一个值是1和3,也就是出现可能前后缀的最小位置。

我们不断地移动i往右,依次O(N)地遍历子串。如果a[i]==a[j+1],则说明左右标记的前缀后缀一样,右移j指针,使前后缀都往右移动了一格然后更新next[i]=j,就是到j的子串的next位置。

OK了,现在来康康while(j&&(a[i]!=a[j+1]))    j=next[j]又是什么呢?我个人认为这是KMP最难以理解的部分,目前网络上面的所有资料都是给你糊过去的!只有我会给你认真解释。

i++,如果a[i]!=a[j+1],就是前缀和后缀已经失配了!怎么办,你看:next表示的是相同前后缀的前缀的位置,所以失配之后,我们需要从0-j字符串(记作s)中寻找一个前缀能够匹配a[i]。我们观察发现:这个j代表的是s串有j长度的前缀和后缀是一样的!将这个相同的前后缀记作s1,s1的长度为j。而s1也有前后缀相同的最大长度,也就是next[j]。

这个前后缀因为是相同的,总共出现了四次,也就是在s的前缀出现2次,在s的后缀也出现了2次!并且s的后缀的后缀必然含有s1!!!所以需要寻找有可能匹配a[i]的a[j+1],无疑是把next[j]赋值给j试试。因为s串的前后都有s1的。如果还是不行,就继续回退,退到j=0退不了为止。

我本来想放一个图在这里,奈何画的实在太丑,弄了个表。

对于abccabc,最长相同前后缀就是abc。然后遇到了a!=c,j跳到0去。对于abccabcabccab,最长相同前后缀就是abccab,此时i=14,j=6,a[i]=a,a[j+1]=c,不匹配!我们回退j=next[j]=2。因为你看:1-6是abccab,8-13也是abccab,它们都有最长的相同前后缀ab。这个点把j退到2,就是比较ab后面的c跟a[i]能不能匹配!!!不能匹配,最后我们后退到了j=0,终于匹配上了。我想你已经完全理解了next的用法。

第二步:跟第一步是差不多的。把子串自我匹配改成了子串匹配主串。

for (RE i=1,j=0;i<=m;i++){while(j&&(j==n||b[i]!=a[j+1])) j=next[j];if (b[i]==a[j+1]) j++;f[i]=j;}

这里我也作个解释,i代表主串的指针,j代表子串的指针。显然子串比主串短,所以j跑到终点,或者是i、j不匹配就回退,如果看不懂你就翻到上面去,我自认为解释得很清楚了。然后用子串跟主串比较即可。f[i]记录的是子串出现的最大长度,如果等于子串长度n代表出现了完整的子串。

来看完整的代码吧,讲得我非常痛苦:

#include<bits/stdc++.h>
using namespace std;
#define RE register int
#define IL inline
#define N 100001
int next[N],f[N],m,n;
char a[100],b[100];
int main(){cin>>(a+1),n=strlen(a+1);for (RE i=2,j=0;i<=n;i++){while(j&&a[i]!=a[j+1]) j=next[j];if (a[i]==a[j+1]) j++;next[i]=j;}cin>>(b+1),m=strlen(b+1);for (RE i=1,j=0;i<=m;i++){while(j&&(j==n||b[i]!=a[j+1]))    j=next[j];if (b[i]==a[j+1]) j++;f[i]=j;}for (RE i=1;i<=m;i++)cout<<f[i];
}

模板题,前几发居然是编辑错误,说next有歧义:

#include<iostream>
#include<cstring>
using namespace std;
#define RE register int
#define IL inline
#define N 1000001
int f[N],m,n;
char a[N],b[N];
int main(){cin>>(a+1)>>(b+1),n=strlen(a+1),m=strlen(b+1);RE j=0;for (RE i=2;i<=m;i++){while(j&&(b[i]!=b[j+1]))  j=f[j];if (b[i]==b[j+1])    j++;f[i]=j;}j=0;for (RE i=1;i<=n;i++){while(j&&a[i]!=b[j+1])   j=f[j];if (a[i]==b[j+1])    j++;if (j==m)   cout<<i-m+1<<endl,j=f[j];}for (RE i=1;i<=m;i++)cout<<f[i]<<" ";
}

我暂时写到这里,因为我今天没空了。

字符串算法:从入门到劝退相关推荐

  1. [刷题]算法竞赛入门经典 3-10/UVa1587 3-11/UVa1588

    书上具体所有题目:http://pan.baidu.com/s/1hssH0KO 题目:算法竞赛入门经典 3-10/UVa1587:Box 代码: //UVa1587 - Box #include&l ...

  2. 提高篇 第二部分 字符串算法 第3章 Trie字典树

    Trie(字典树)解析及其在编程竞赛中的典型应用举例 - Reqaw - 博客园 『一本通』Trie字典树 - YeLingqi - 博客园 字典树(Trie Tree) - 仰望高端玩家的小清新 - ...

  3. 《算法竞赛入门经典训练指南》pdf

    下载地址:网盘下载 基本介绍 编辑 内容简介 <算法竞赛入门经典:训练指南>题目多选自近年来ACM/ICPC区域赛和总决赛真题,内容全面,信息量大,覆盖了常见算法竞赛中的大多数细分知识点. ...

  4. ICPC程序设计题解书籍系列之九:罗勇军《算法竞赛入门到进阶》

    罗书<算法竞赛入门到进阶>题目一览 第1章 算法竞赛概述 HDU1000 HDU1089-HDU1096 A+B for Input-Output Practice (I)-(VIII)( ...

  5. (Step1-500题)UVaOJ+算法竞赛入门经典+挑战编程+USACO

    下面给出的题目共计560道,去掉重复的也有近500题,作为ACMer Training Step1,用1年到1年半年时间完成.打牢基础,厚积薄发. 一.UVaOJ http://uva.onlinej ...

  6. 刘汝佳《算法竞赛入门经典》---总结

    刘汝佳:<算法竞赛入门经典> 三步: 基本的数据结构+算法知识: 数论等数学基本知识: 锻炼联想建模能力.知识与实际相结合,解决实际问题! 第一章:程序设计入门 1.a/b 当a.b为整数 ...

  7. 算法竞赛入门经典(刘汝佳)——代码笔记

    Reference: <算法竞赛入门经典>(刘汝佳)第一版.第二版 ------------------------------------------------------------ ...

  8. 算法竞赛入门【码蹄集进阶塔335题】(MT2001-2025)

    算法竞赛入门[码蹄集进阶塔335题](MT2001-2025) 文章目录 算法竞赛入门[码蹄集进阶塔335题](MT2001-2025) 前言 为什么突然想学算法了? 为什么选择码蹄集作为刷题软件? ...

  9. 算法竞赛入门经典(第二版)第三章习题

    声明:作者水平有限,只是会基础C语言的小菜,C++还未入门.作者仅根据算法竞赛入门经典(第二版)书上第三章习题所述题意而编写,并未严格按照原题的输入输出编写,代码仅经过个人测试(OJ网站太慢了).代码 ...

最新文章

  1. 谈谈我开发过的几套语音通信解决方案
  2. arduino nano 蓝牙_用Arduino玩转掌控板(ESP32):ESP32概述与Arduino软件准备
  3. 兼容ie8 rgba()用法
  4. sql server 主从数据库同步 利用发布 订阅是实现
  5. Path does not chain with any of the trust anchors
  6. 用VS向SharePoint中部署添加List 并指定应用的Content Type
  7. [Redis6]NoSQL数据库简介_特点
  8. 【Docker】容器镜像有哪些特性
  9. OpenGL 人物走动源码
  10. 关联分析算法(二)——FP-growth算法与python用法
  11. spring加载jar包中多个配置文件(转)
  12. Eclipse SVN插件Subclipse和Subversive简介
  13. SQL语句设置标识种子
  14. ClientToScreen ()与 ScreenToClient()
  15. 怎样使用裁剪图片软件随意裁剪图片尺寸?
  16. linux网卡驱动realtek,Linux系统Realtek网卡驱动安装
  17. 暗色调Xshell配色方案
  18. OEE(设备综合效率)
  19. 转载:Max vs Maya
  20. android动手写平滑滚动歌词控件

热门文章

  1. 用计算机弹猴哥,《西游记》孙悟空获“弼马温”一职,网友弹屏吐槽:猴哥被忽悠了...
  2. 区块链P2P网络协议演进过程
  3. 用Eclipse搭建VLC SDK开发环境
  4. 程序员跳槽B站遭老东家索赔200万,法院判定无需赔偿,竞业限制不应阻碍工程师再就业...
  5. 从零维到十维空间如何在纸上用手绘出来
  6. 【Android工具】更新观影渠道,安卓、iOS、PC三端通用,免费电影在线观看
  7. sqlserver 模糊查询
  8. 【程序员讲装修】平台选择第三期
  9. 形式逻辑(03)联言判断 和 推理
  10. Android Binder学习指南