[JZOJ3159][P5254]「JSOI2013」『字符串哈希』『枚举』广告计划

题目

如今,在建筑的墙面上或者篱笆桩的表面上涂上一些广告,是一种新的吸引眼球的方法。

现在,小G 运营的一家小公司,决定也试着这样做做广告。小G 在他的篱笆桩上腾出了一些地方供广告使用。每一个篱笆桩都是一个水平的11L 的4 棱柱,其中有一个1L 的面是可以做广告的。1L 的面上划出了L 个1*1 的小正方形(更具体地说是连续L 个水平排列的正方形),每个正方形内写上一个字母。

​ 时间久了,广告做多了难免会出现一些比较麻烦的情况,比如计划改变或者制作出错,因此小G 的仓库里面积累了好多没有用的,上面已经写上L 个字母的篱笆桩。(所有的篱笆桩的大小都是一样的,他们唯一的区别仅仅在于上面写了什么字母)。

​ 小G 决定对于这些篱笆桩进行重新利用,并且有了一些新的想法。

​ 如果将这些篱笆桩竖直的叠放起来,并且依次从左往右,对于每一个篱笆桩顺次从上到下读出上面的字母,那么我们可以得到一些新的比较长的单词,如下图:

​ 这些新的单词能满足小G 的一些新的需要。当然,基于美学考虑,小G 是不允许你删改篱笆桩上已经写上的字母的。

​ 我们更具体地描述这个过程。我们将K 个长度为L 的篱笆桩叠在一起,可以得到一个写有K行L 列共K*L 个字母的面,每一个字母都在对应的唯一的格子里。我们从左上角开始依次向下读出每一个字母可以得到一个字母的序列,比如上图中的这个例子,那么我们读出的结果就是“TOEIIZENITKN”。如果,这个串中有我们所需要的单词,那么显然我们只需要将一些格子刷白,就可以得到我们所需要的了。举个例子,比如小G 想要给圣彼得堡的足球队泽尼特队做个广告,那么很显然只要按照上图中的做法就可以达到小G 想要的效果了。

​ 现在小G已经想好了要做怎样的广告,同时也提供给你了小G仓库中的篱笆桩的类型的描述,你可以认为每一种类型的篱笆桩都是有无数个的。现在小G 想知道至少需要多少个篱笆桩叠起来才可以做出小G 想要的广告。

1 ≤ N , L ≤ 100 1\le N,L\le 100 1N,L100, 1 ≤ ∣ s ∣ ≤ 200 1\le |s|\le200 1s200

题解

题目大意:有 N N N种长度为 L L L的字符串和一个匹配串 s s s,每种都有无数个,现选出 K K K字符串,从上到下排成一个 K × L K\times L K×L的字符矩阵,从左上角开始,从上到下,从左到右顺序写下各个字符,得出一个长度为 K × L K\times L K×L的字符串 S S S,且 s s s S S S的子串,最小化 K K K和一种排列方案

考虑字符串哈希,将所有子串用27进制压缩存到哈希表中(即每个字符串的任意位置开头和任意位置结尾),同时记录每个串所在的字符串的编号和开头所在的位置。

从小到大枚举 K K K,那么第一个合法 K K K就是答案。找出当前 K K K下匹配串 s s s被分成的 K K K个小串

如: s = abcde , K = 3 s=\text{abcde},K=3 s=abcde,K=3

3个小串分别是 ad , be , c \text{ad},\text{be},\text{c} ad,be,c

将这 K K K个小串以上述方法压缩,在哈希表中查找

若第 i i i个小串在第 u u u个字符串中,开头在第 j j j列,那么 b z [ i ] [ j ] = u bz[i][j]=u bz[i][j]=u,表示第 i i i个小串的开头在第 j j j列可以在第 u u u个字符串中找到

再枚举 s s s的第一个子串的开头的行 i i i,列 j j j,在 b z bz bz中查找,如果第 j j j或者 j + 1 j+1 j+1列都有对应的 u u u值,那么就是合法答案

也就是说,在这种情况下,如果一些 b z [ u ] [ j ] bz[u][j] bz[u][j]和另一些 b z [ u ] [ j + 1 ] bz[u][j+1] bz[u][j+1]都有值( u u u表示 s s s的第 u u u个子串),那么就合法( j j j还是 j + 1 j+1 j+1 i i i

Code

#include<cstdio>
#include<cstring>
#define md 2999999
#define hashmd 5100001 
using namespace std;
int n,l,m,id,now,num,mi[105],pos[505001][2],nxt[505001],head[5100001],hs[5100001],bj[205][205],c[205][205];
bool flag;
char s[105][105],ss[205];
void add(int x,int i,int j)
{
	pos[++num][0]=i;pos[num][1]=j;
	nxt[num]=head[x];
	head[x]=num;
}
void hash(int x,int i,int j)
{
	int p=x;
	while (hs[p])
	{
		if (hs[p]==x)
		{
			add(p,i,j);
			return;
		}
		p=(p+1)%hashmd;
	}
	hs[p]=x;
	add(p,i,j);
}
void find(int x,int r)
{
	int p=x;
	while (hs[p])
	{
		if (hs[p]==x)
		{
			for (int i=head[x];i;i=nxt[i])
				c[r][pos[i][1]]=pos[i][0],bj[r][pos[i][1]]=id;
			return;
		}
		p=(p+1)%hashmd;
	}
}
int main()
{
	mi[0]=1;
	for (int i=1;i<=100;++i)
		mi[i]=mi[i-1]*27%md;
	scanf("%d%d",&n,&l);
	for (int i=1;i<=n;++i)
	{
		scanf("%s",s[i]+1);
		for (int j=1;j<=l;++j)
		{
			int x=1;
			for (int k=j;k<=l;++k)
			{
				x=(x+(s[i][k]-'a'+1)*mi[k-j+1]%md)%md;
				hash(x,i,j);
			}
		}
	}
	scanf("%s",ss+1);
	m=strlen(ss+1);
	for (int k=1;k<=m;++k)
	{
		++id;
		for (int j=1;j<=k;++j)
		{
			int x=1,ln=0;
			now=j;
			while (now<=m)
			{
				x=(x+(ss[now]-'a'+1)*mi[++ln]%md)%md;
				now+=k;	
			}
			find(x,j);	
		} 
		for (int i=1;i<=k;++i)
		{
			int h=k-i+1;
			for (int j=1;j<=l;++j)
			{
				flag=true;
				for (int u=1;u<=k;++u)
				{
					if ((u<=h&&bj[u][j]<id)||(u>h&&bj[u][j+1]<id))
					{
						flag=false;
						break;
					}
				}
				if (flag)
				{
					printf("%d\n",k);
					for (int u=h+1;u<=k;++u) printf("%d ",c[u][j+1]);
					for (int u=1;u<=h;++u) printf("%d ",c[u][j]);
					return 0;
				}
			}
		}
	}
	printf("-1\n");
	return 0;
}

感谢LZA的博客提供的帮助,ORZ:

https://blog.csdn.net/qq_39565901/article/details/87472261

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据引用[1],dp[u][j]表示在u子树中选取恰好j个人时能获得的最大价值。而根据引用,该问题的时间复杂度为O(log2​104×nm)。 对于洛谷P2143 [JSOI2010] 巨额奖金问题,我们可以使用动态规划来解决。具体步骤如下: 1. 首先,我们需要构建一棵树来表示员工之间的关系。树的根节点表示公司的总经理,其他节点表示员工。每个节点都有一个权值,表示该员工的奖金金额。 2. 接下来,我们可以使用动态规划来计算每个节点的dp值。对于每个节点u,我们可以考虑两种情况: - 如果选择节点u,则dp[u][j] = dp[v][j-1] + value[u],其中v是u的子节点,value[u]表示节点u的奖金金额。 - 如果不选择节点u,则dp[u][j] = max(dp[v][j]),其中v是u的子节点。 3. 最后,我们可以通过遍历树的所有节点,计算出dp[u][j]的最大值,即为所求的巨额奖金。 下面是一个示例代码,演示了如何使用动态规划来解决洛谷P2143 [JSOI2010] 巨额奖金问题: ```python # 构建树的数据结构 class Node: def __init__(self, value): self.value = value self.children = [] # 动态规划求解最大奖金 def max_bonus(root, j): dp = [[0] * (j+1) for _ in range(len(root)+1)] def dfs(node): if not node: return for child in node.children: dfs(child) for k in range(j, 0, -1): dp[node.value][k] = max(dp[node.value][k], dp[node.value][k-1] + node.value) for child in node.children: for k in range(j, 0, -1): for l in range(k-1, -1, -1): dp[node.value][k] = max(dp[node.value][k], dp[node.value][k-l-1] + dp[child.value][l]) dfs(root) return dp[root.value][j] # 构建树 root = Node(1) root.children.append(Node(2)) root.children.append(Node(3)) root.children[0].children.append(Node(4)) root.children[0].children.append(Node(5)) root.children[1].children.append(Node(6)) # 求解最大奖金 j = 3 max_bonus_value = max_bonus(root, j) print("最大奖金为:", max_bonus_value) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值