Note4

目录

    • KMP
    • Trie(字典树)
    • AC自动机
    • 二维前缀和+二维差分
    • 差分约束系统
    • 矩阵快速幂
    • 公共凸包 Andrew算法
    • bitset
    • dp方程
      • · 最大连续子段和
    • 二分模板

KMP

时间复杂度为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";
}

Trie(字典树)

详解链接

//实现功能:输入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"); 
	}
}

AC自动机

视频链接
详解链接

//实现功能:给定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算法

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
类型在定义时就需要指定所占的空间,例如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方程

· 最大连续子段和

	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 _
}

你可能感兴趣的:(My,Notes)