时间复杂度为O(m+n),即主串长+模式串长
详解链接
//从主串中找模式串t出现的所有位置
#include
using namespace std;
const int N=1000007;
char t[N],p[N]; //t是主串,p是模式串
int next[N],flag;
void get_next(char p[],int next[]){
int len=strlen(p),i=0,j=-1;
next[0]=-1;
while(i<len){
if(j==-1||p[i]==p[j]){
i++;
j++;
if(p[i]!=p[j])next[i]=j;
else next[i]=next[j]; //(next[i]=j;)的优化
}else j=next[j];
}
}
void kmp(char p[],int next[]){
get_next(p,next);
int i=0,j=0;
int t_len=strlen(t),p_len=strlen(p);
while(i<t_len&&j<p_len){
if(j==-1||t[i]==p[j]){
i++;
j++;
}else j=next[j];
if(j==p_len){
printf("%d\n",i-j); //以0为首地址的位置
//return; //找字符串t出现的第一个位置即可的话直接return
flag=1;
j=next[j]; //继续匹配
}
}
}
int main(){
cin>>t>>p;
kmp(p,next);
//if(!flag)cout<<"Not Find";
}
详解链接
//实现功能:输入n个串,m次查找某个串是否出现过
//n<=10000,m<=100000,每个串长度<=50,只包含小写字母
//trie是以空间换时间,下面的N是节点数
//search可以改一下用来求某个前缀是否出现过
#include
using namespace std;
const int N=500010;
struct Trie{ //用struct封装
int ch[N][26],id,val[N],end[N];
//ch是子节点,id是编号,val存维护的信息,end判断是否是一个结束
Trie(){
id=1;
memset(ch[0],0,sizeof(ch[0]));
memset(val,0,sizeof(val));
memset(end,0,sizeof(end));
}//初始化
void insert(char *s,int value){
int u=0,len=strlen(s);
for(int i=0;i<len;i++){
int v=s[i]-'a';
if(!ch[u][v])ch[u][v]=id++;
u=ch[u][v]; //继续向下遍历
}
//val[u]=value; 存维护的信息
end[u]=1;
}
int search(char *s){
int u=0,len=strlen(s);
for(int i=0;i<len;i++){
int v=s[i]-'a';
if(!ch[u][v])return 0;
u=ch[u][v];
}
if(end[u])return 1;else return 0;
}
}tree;
int n,m;
char s[100];
int main(){
cin>>n;
for(int i=1;i<=n;i++){
scanf("%s",s);
tree.insert(s,1);
}
cin>>m;
for(int i=1;i<=m;i++){
scanf("%s",s);
int k=tree.search(s);
if(k)printf("exit\n");
else printf("not find");
}
}
视频链接
详解链接
//实现功能:给定n个模式串和1个文本串,求有多少个模式串在文本串里出现过
#include
using namespace std;
const int N=500010;
struct AC{ //用struct封装
int ch[N][26],val[N],fail[N],id;
void insert(char *s){ //构造Trie树
int len=strlen(s),u=0;
for(int i=0;i<len;i++){
int v=s[i]-'a';
if(!ch[u][v])ch[u][v]=++id;
u=ch[u][v];
}
val[u]++;
}
void build(){ //bfs构造fail指针
queue<int>q;
for(int i=0;i<26;i++)
if(ch[0][i]){
fail[ch[0][i]]=0;
q.push(ch[0][i]);
}
while(!q.empty()){
int u=q.front();q.pop();
for(int i=0;i<26;i++)
if(ch[u][i]){
fail[ch[u][i]]=ch[fail[u]][i];
q.push(ch[u][i]);
}else ch[u][i]=ch[fail[u]][i];
}
}
int query(char *s){
int len=strlen(s),u=0,ans=0;
for(int i=0;i<len;i++){
u=ch[u][s[i]-'a'];
for(int t=u;t&&~val[t];t=fail[t])ans+=val[t],val[t]=-1;
//一直向下寻找,直到匹配失败(失败指针指向根或者当前节点已找过).
//将遍历过后的节点信息标记为-1,防止重复计算
}
return ans;
}
}ac;
int n;
char p[1000007];
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%s",p),ac.insert(p);
ac.build();
scanf("%s",p);
int ans=ac.query(p);
cout<<ans;
}
二维差分(d是差分数组),对d求前缀和即为真实值
void deal(int xl,int yl,int xr,int yr,int val){
++d[xl][yl];
--d[xr+1][yl];
--d[xl][yr+1];
++d[xr+1][yr+1];
}
二维前缀和
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
cin>>sum[i][j];
sum[i][j]+=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1];
}
//求(x1,y1)到(x2,y2)的矩形内数的和(包含边上)
ans=sum[x2][y2]-sum[x1-1][y1]-sum[x1][y1-1]+sum[x1-1][y1-1];
[差分约束系统—详解]
例题:Intervals
题意:给n个区间每个区间的范围为[ai,bi],让你确定k个整数,使这k个整数在第i个区间[ai,bi]至少有ci个共同的数。(0<=ai<=bi<=50000)
思路:设dis[i]为[0…i]有几个数被选中,则可得到不等式
①dis[bi]-dis[ai-1]>=ci
②0<=dis[i+1]-dis[i]<=1 后半部分即dis[i]-dis[i+1]>=-1
对dis[bi]-dis[ai-1]>=ci、dis[i+1]-dis[i]>=0、dis[i]-dis[i+1]>=-1建边,SPFA跑最长路。dis[maxb] 即为答案
建边方法:
对于不等式dis[u]-dis[v]>=w即dis[u]>=dis[v]+w,令w为v→u的边,SPFA跑最长路后此式成立(因为SPFA完成之后dis[u]是源点到u的最长距离,dis[v]是源点到v的最长距离,dis[u]显然>=dis[v]+w(v,w)
所以对于dis[ui]-dis[vi]>=wi的不等式组,addedge(vi,ui,wi),跑最长路
___ 对于dis[ui]-dis[vi]<=wi的不等式组,addedge(vi,ui,wi),跑最短路
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define Inf 10000000000000
typedef long long ll;
const int N=50007,M=100007;
int n,mina=N,maxb,vis[N],head[N],cnt;
ll dis[N];
struct EDGE{int v,w,next;}e[M*2];
void addedge(int u,int v,ll w){
e[cnt].v=v;
e[cnt].w=w;
e[cnt].next=head[u];
head[u]=cnt++;
}
void spfa(int s){//最长路
for(int i=mina;i<=maxb;i++)dis[i]=(i==s?0:-Inf);
vis[s]=1;
queue<int>q;
q.push(s);
while(!q.empty()){
int now=q.front();q.pop();
vis[now]=0;
for(int i=head[now];i!=-1;i=e[i].next)
if(dis[e[i].v]<dis[now]+e[i].w){
dis[e[i].v]=dis[now]+e[i].w;
if(!vis[e[i].v]){
q.push(e[i].v);
vis[e[i].v]=1;
}
}
}
}
int main(){
while(cin>>n){
memset(head,-1,sizeof(head));
memset(vis,0,sizeof(vis));
cnt=0;
for(int i=1;i<=n;i++){
int a,b;ll c;
scanf("%d%d%lld",&a,&b,&c);
a++;b++;//由于最小值可能为0,所以防止我的起点为负数
mina=min(mina,a-1);
maxb=max(maxb,b);
addedge(a-1,b,c);
}
for(int i=mina;i<maxb;i++){
addedge(i+1,i,-1);
addedge(i,i+1,0);
}
spfa(mina);
cout<<dis[maxb]<<"\n";
}
}
[ 矩阵快速幂 ] 学习笔记
用struct封装的矩阵,
注意:当mod为素数时,对指数取模是e%(mod-1),然后base=base^(e+mod-1) [费马小定理]
使用方法:
const int N=矩阵大小+7;
ll n,mod;
int main(){
Matrix base;
ll arr[ ]={写出来第一项数组A1};
base=base^(n-1);
ll ans=0;
for(int i=1;i<=size;i++)ans=(ans+base.m[1][i]*arr[i])%mod;
cout<<ans;
}
模板:
struct Matrix{
ll m[N][N];
int size=3; //矩阵实际大小
Matrix(){//根据题目进行初始化
memset(m,0,sizeof(m));
m[1][1]=1;m[1][2]=2;m[1][3]=1;
m[2][1]=1;m[2][2]=0;m[2][3]=0;
m[3][1]=0;m[3][2]=0;m[3][3]=1;
}
void clear(){
memset(m,0,sizeof(m));
for(int i=1;i<=size;i++)
m[i][i]=1;
}
void display(){
cout<<"Matrix's begin:"<<endl;
for(int i=1;i<=size;i++)
for(int j=1;j<=size;j++)
if(j<size) cout<<m[i][j]<<" ";
else cout<<m[i][j]<<endl;
cout<<"Matrix's end:"<<endl;
}
friend Matrix operator*(Matrix a,Matrix b){
Matrix ans;
for(int i=1;i<=size;i++) //size*size为矩阵大小
for(int j=1;j<=size;j++){
ans.m[i][j]=0;
for(int k=1;k<=size;k++)
ans.m[i][j]=(ans.m[i][j]+a.m[i][k]*b.m[k][j])%mod;
}
return ans;
}
friend Matrix operator^(Matrix base,ll k){
Matrix ans;
ans.clear();
while(k){
if(k&1) ans=ans*base;
base=base*base;
k>>=1;
}
return ans;
}
};
Andrew算法图文详解
[例题:Codeforces-116B Polygons]
例题题意:给一个多边形点集A,和一个多边形点集B,问多边形B是否完全包含在多边形A内
例题思路:对A+B所有点建立一个公共凸包,二分判断公共凸包上是否有B中的点,若无则说明小多边形完全包含在大多边形内,输出YES;否则不完全包含,输出NO。
#include
using namespace std;
const int N=1e5+7,M=2e4+7;
const double eps=1e-8;
int eps_cmp(double x){return (x>eps)-(x<-eps);}
struct point{
double x,y;
point(){}
point(double _x,double _y){x=_x;y=_y;}
point operator -(const point &b)const{
return point(x-b.x,y-b.y);
}
bool operator ==(const point &b)const{
return eps_cmp(x-b.x)==0&&eps_cmp(y-b.y)==0;
}
bool operator <(const point &b)const{
return x<b.x||x==b.x&&y<b.y;
}
double operator *(const point &b)const{//叉积
return x*b.y-b.x*y;
}
double mod(){return sqrt(x*x+y*y);}
}res[N+M],p[N+M],pb[M];
int andrew(int n,point p[]){//res存凸包,下标从1到cnt,res[cnt]=res[1]
sort(p+1,p+1+n);
int cnt=0;
for(int i=1;i<=n;i++){
while(cnt>1&&(res[cnt]-res[cnt-1])*(p[i]-res[cnt-1])<0)cnt--;//叉积判断是否丢掉res[cnt]
res[++cnt]=p[i];
}
int tmp=cnt;
for(int i=n-1;i>=1;i--){
while(cnt>tmp&&(res[cnt]-res[cnt-1])*(p[i]-res[cnt-1])<0)cnt--;
res[++cnt]=p[i];
}
return cnt;
}
int n,m;
int main(){
cin>>n;
for(int i=1;i<=n;i++)scanf("%lf%lf",&p[i].x,&p[i].y);
cin>>m;
for(int i=1;i<=m;i++){
scanf("%lf%lf",&pb[i].x,&pb[i].y);
p[++n]=pb[i];
}
int cnt=andrew(n,p),flag=1;
sort(pb+1,pb+1+m);
for(int i=1;i<=cnt;i++){//二分判断公共凸包上是否有pb中的点
int t=lower_bound(pb+1,pb+1+m,res[i])-pb;
if(pb[t]==res[i]){flag=0;break;}
}
if(flag)cout<<"YES";else cout<<"NO";
}
bitset头文件就是bitset
类型在定义时就需要指定所占的空间,例如bitset<500>bit;
bitset类型可以用string和整数初始化(整数转化成对应的二进制)
#include
#include
#include
#include
using namespace std;
int main()
{
bitset<23>bit (string("11101001"));
cout<<bit<<endl;
bit=233;
cout<<bit<<endl;
return 0;
//输出为
//00000000000000011101001
//00000000000000011101001
}
bitset支持所有位运算
bitset<23>bita(string("11101001"));
bitset<23>bitb(string("11101000"));
cout<<(bita^bitb)<<endl;
//输出00000000000000000000001
bitset<23>bita(string("11101001"));
bitset<23>bitb(string("11101000"));
cout<<(bita|bitb)<<endl;
//输出00000000000000011101001
bitset<23>bita(string("11101001"));
bitset<23>bitb(string("11101000"));
cout<<(bita&bitb)<<endl;
//输出00000000000000011101000
bitset<23>bit(string("11101001"));
cout<<(bit<<5)<<endl;
//输出00000000001110100100000
bitset<23>bit(string("11101001"));
cout<<(bit>>5)<<endl;
//输出00000000000000000000111
常用函数
对于一个叫做bit的bitset:
bit.size() 返回大小(位数)
bit.count() 返回1的个数
bit.any() 返回是否有1
bit.none() 返回是否没有1
bit.set() 全都变成1
bit.set(p) 将第p + 1位变成1(bitset是从第0位开始的!)
bit.set(p, x) 将第p + 1位变成x
bit.reset() 全都变成0
bit.reset(p) 将第p + 1位变成0
bit.flip() 全都取反
bit.flip(p) 将第p + 1位取反
bit.to_ulong() 返回它转换为unsigned long的结果,如果超出范围则报错
bit.to_ullong() 返回它转换为unsigned long long的结果,如果超出范围则报错
bit.to_string() 返回它转换为string的结果
dp[i]=max(dp[i-1]+a[i],a[i]);
ans=max(ans,dp[i]);
二分至剩余2个数,再check取舍。
int divid(int l,int r,int need){
int mid;
while(r-l>1){
mid=(l+r)/2;
if(check(mid)==need)_
else _
}
if(check(_)==need)return _
else return _
}