0x10基本数据结构

0x10基本数据结构

0x11 栈

例题:Editor

“对顶栈”——维护两个栈,栈顶相对,光标即为两个栈的栈顶,移动光标即为弹出左(或右)栈顶并将其加入到另一栈中。删除即弹出左栈顶。另外用一数组维护前缀和,弹栈,进栈时相应加入、删除。

#include<bits/stdc++.h>
using namespace std;

const int inf=0x3f3f3f3f;
const int maxn=1e6+5;
int q,pre[maxn],sum,cnt,m[maxn];
char op[10];

int main()
{
	//freopen("input.txt","r",stdin);
	while(scanf("%d",&q)!=EOF)
	{
		stack<int>l;
		stack<int>r;
		int x,y,k;
		sum=0;
		m[0]=-inf;
		while(q--)
		{
			scanf("%s",op);
			if(op[0]=='I')
			{
				scanf("%d",&x);
				l.push(x);
				//++cnt;
				//pre[cnt]=pre[cnt-1]+x;
				sum+=x;
				cnt=l.size();
				m[cnt]=max(m[cnt-1],sum);
			}
			else if(op[0]=='D'&&!l.empty()) 
			{
				x=l.top();
				l.pop();
				sum-=x;
			}
			else if(op[0]=='L')
			{
				if(!l.empty())
				{
					x=l.top();
					l.pop();
					r.push(x);
					sum-=x;
				}
			}
			else if(op[0]=='R')
			{
				if(!r.empty())
				{
					x=r.top();
					r.pop();
					l.push(x);
					sum+=x;
					cnt=l.size();
					m[cnt]=max(m[cnt-1],sum);
				}
			}
			else 
			{
				scanf("%d",&k);
				printf("%d\n",m[k]);
			}
		}
	}
	return 0;
} 
例题:Largest Rectangle in Histogram 直方图中最大的矩形

单调栈:当加入不符合上升的矩形高度时维护出前面比该矩形高的矩形可组成的面积,该矩形继承被弹出栈矩形的宽度。
保证栈中矩形高度递增,若当前矩形高度小于栈顶,则把栈顶比它高的矩形全部弹出,因为在它之后可组成的矩形高度已经受该矩形高度限制,与之前比它高的矩形已经无关了。以某个高度的矩形为高的子矩形是在第一次遇见比该矩形矮的第一个矩形的时候计算的。

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const ll maxn=100005;
ll n,cnt,ans,sum;
ll h,w[maxn],s[maxn];

int main()
{
	//freopen("input.txt","r",stdin);
	//freopen("output.txt","w",stdout);
	while(1)
	{
		scanf("%lld",&n);
		if(!n) break;
		memset(w,0,sizeof(w));
		cnt=ans=sum=0;
		for(ll i=1;i<=n+1;i++)
		{
			if(i<=n) scanf("%lld",&h);
			else h=0; 
			if(h>s[cnt]) 
			{
				s[++cnt]=h;
				w[cnt]=1;
			}
			else
			{
				sum=0;
				while(s[cnt]>h)
				{
					sum+=w[cnt];
					ans=max((ll)ans,(ll)s[cnt]*sum);
					--cnt;
				}
				s[++cnt]=h;w[cnt]=sum+1;
			}
		}
		printf("%lld\n",ans);
	}
	return 0;
}

0x12 队列

例题:最大子序和

单调队列:“我比你年轻,我又比你优秀,那你肯定就没用了。”——ouyang
这里的“年轻”指某一个数比先前的数后出现,“存活”时间更长。而我们要维护最大序列,运用前缀和pre数组,当序列右端的p数固定(即被遍历的数),左端的pre值越小越好,即“更优秀”。
遍历序列,每到一个数即判断队列中哪些元素老死了超出限定长度了,将其弹出。一个新数比它之前的、队列里的数年轻,而又如果比队列里的数优秀,则将队列里不如它优秀的数弹出,将年轻又优秀的数入队。
更新答案时用队列左端,即pre值最小的数。而在它之后加入队列的“年轻”的数是在以后有希望更新答案的数。

#include<bits/stdc++.h>
using namespace std;

const int maxn=300005;
int n,m,l,r,ans;
int q[maxn],pre[maxn];

int main()
{
	scanf("%d%d",&n,&m);
	l=r=1;
	for(int x,i=1;i<=n;i++)
	{
		scanf("%d",&x);
		pre[i]=pre[i-1]+x;
	}
	for(int i=1;i<=n;i++)
	{
		while(l<=r&&q[l]<i-m) ++l;
		ans=max(ans,pre[i]-pre[q[l]]);
		while(l<=r&&pre[q[r]]>=pre[i]) --r;
		q[++r]=i;
	}
	printf("%d",ans);
	return 0;
}

0x13 链表与邻接表

0x14 Hash

例题:兔子与兔子

取一固定值p做进制数,用一个大于零的数值代表每种字符。对一个字符串用p进制表示,取一固定值m求出p进制数对m的余数作为该字符串的Hash值。一般p取131或13331,Hash值不易起冲突。存储用unsigned long long 存储数据大且不用取模(自动对264取模)

#include<bits/stdc++.h>
using namespace std;

typedef unsigned long long ll;
const int maxn=1000005;
char s[maxn];
ll m,l1,l2,r1,r2,n;
ll p[maxn],f[maxn];

int main()
{
	scanf("%s",s+1);
	n=strlen(s+1);
	p[0]=1;
	for(int i=1;i<=n;i++)
	{
		f[i]=f[i-1]*131+(s[i]-'a'+1);
		p[i]=p[i-1]*131;
	}
	scanf("%lld",&m);
	while(m--)
	{
		scanf("%lld%lld%lld%lld",&l1,&r1,&l2,&r2);
		if(f[r1]-p[r1-l1+1]*f[l1-1]==f[r2]-p[r2-l2+1]*f[l2-1]) printf("Yes\n");
		else printf("No\n");
	}
	return 0;
}

0x15 字符串

例题(非书上内容):Luogu P1470 最长前缀 Longest Prefix

KMP算法

#include<bits/stdc++.h>
using namespace std;

const int maxn=2e5+5;
char c[205][20],s[maxn],a[maxn],k[maxn];
int nex[maxn],ans[maxn],cnt,n,m;

int main()
{
	//freopen("input.txt","r",stdin);
	while(c[cnt][1]!='.') 
		scanf("%s",c[++cnt]+1);
	--cnt;
	while(scanf("%s",k)!=EOF)
	{
		int kk=strlen(k);
		for(int i=0;i<kk;i++) s[++n]=k[i];
	}
	for(int k=1;k<=cnt;k++)
	{
		memset(nex,0,sizeof(nex));
		m=strlen(c[k]+1);
		memcpy(a+1,c[k]+1,sizeof(c[cnt]+1));
		int j=0;
		for(int i=2;i<=m;i++)
		{
			while(j&&a[i]!=a[j+1]) j=nex[j];
			if(a[i]==a[j+1]) ++j;
			nex[i]=j;
		}
		j=0;
		/*for (int i=0;i<n;i++)  //KMP
        {
            while (j&&c[k][j+1]!=s[i+1]) j=nex[j];
            if (c[k][j+1]==s[i+1]) j++;
            if (j==m)
            {
                ans[i+2]--;
                ans[i-m+2]++;
                j=nex[j];
            }
        }*/
		for(int i=1;i<=n;i++)
		{
			while(j&&s[i]!=c[k][j+1])  j=nex[j];
			if(s[i]==c[k][j+1]) ++j;
			if(j==m)
			{
				++ans[i-m+1];
				--ans[i+1];
				j=nex[j];
			}
		}
	}
	for(int i=1;i<=n;i++)
	{
		ans[i]+=ans[i-1];
		if(ans[i]<=0)
		{
			printf("%d",i-1);
			return 0;
		}
	}
	printf("%d",n);
	return 0;
}

0x16 Trie

例题: 前缀统计

跑一遍trie树,但记录结尾标记的end数组变成累加结尾前缀。而且……
end在acwing上是关键字? 编译错误??

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const ll maxn=1e6+5;
ll n,m,trie[maxn][26],cnt=1,ed[maxn],sum;
char s[maxn],t[maxn],c[maxn];

void ins()
{
	ll len=strlen(c+1),p=1;
	for(ll i=1;i<=len;i++)
	{
		ll ch=c[i]-'a';
		if(!trie[p][ch]) trie[p][ch]=++cnt;
		p=trie[p][ch];
	}
	++ed[p];
}

ll search()
{
	ll len=strlen(c+1),p=1;
	sum=0;
	for(ll i=1;i<=len;i++)
	{
		ll ch=c[i]-'a';
		if(!p) break;
		p=trie[p][ch];
		sum+=ed[p];
	}
	return sum;
}

int main()
{
	//freopen("input.txt","r",stdin);
	scanf("%lld%lld",&n,&m);
	for(ll i=1;i<=n;i++) 
	{
		scanf("%s",c+1);
		ins();
	}
	while(m--)
	{
		scanf("%s",c+1);
		printf("%lld\n",search());
	}
	return 0;
}

0x17 二叉堆

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值