「JSOI2007」字符加密 后缀数组(SA)

字符加密

题目大意:寻找最小的循环移动位置

解题思路:
后缀数组模板题,将字符串 S复制一份,接在S后面变成 SS 就转化成了后缀排序问题,每个字符串的最后一个字符对应的就是s[sa[i]+n-1],然后跑一下模板就odk。

Code:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define mk makr_pair
#define ll long long
using namespace std;
inline int read()
{
  int x=0,f=1;char ch=getchar();
  while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();}
  while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
  return x*f;
}
const int maxn = 2e6+1e2;
int rk[maxn],wb[maxn],sa[maxn];
int tmp[maxn],h[maxn],height[maxn];
int n;
char a[maxn];
void getsa()
{
 int *x =rk,*y=tmp;
 int s = 128;
 for (int i=1;i<=n;i++) x[i]=a[i],y[i]=i;
 for (int i=1;i<=s;i++) wb[i]=0;
 for (int i=1;i<=n;i++) wb[x[y[i]]]++;
 for (int i=1;i<=s;i++) wb[i]+=wb[i-1];
 for (int i=n;i>=1;i--) sa[wb[x[y[i]]]--]=y[i];
 int p =0;
 for (int j=1;p<n;j<<=1)
 {
  p=0;
  for (int i=n-j+1;i<=n;i++) y[++p]=i;
  for (int i=1;i<=n;i++) if (sa[i]>j) y[++p] = sa[i]-j;
  for (int i=1;i<=s;i++) wb[i]=0;
  for (int i=1;i<=n;i++) wb[x[y[i]]]++;
  for (int i=1;i<=s;i++) wb[i]+=wb[i-1];
     for (int i=n;i>=1;i--) sa[wb[x[y[i]]]--]=y[i];
  swap(x,y);
  p=1;
  x[sa[1]]=1;
  for (int i=2;i<=n;i++)
  {
   x[sa[i]] = (y[sa[i]]==y[sa[i-1]] && y[sa[i]+j]==y[sa[i-1]+j]) ? p : ++p; 
   } 
   s=p;
 }
 for (int i=1;i<=n;i++) rk[sa[i]]=i;
 h[0]=0;
    for (int i=1;i<=n;i++)
 {
  h[i]=max(h[i-1]-1,0);
  while(i+h[i]<=n && sa[rk[i]-1]+h[i]<=n && a[i+h[i]]==a[sa[rk[i]-1]+h[i]]) h[i]++;
 } 
 for (int i=1;i<=n;i++) height[i]=h[sa[i]];
}
/*
void getsa()
{
	
	int *x =rk,*y=tmp;
	int s = 128;
	for (int i=1;i<=n;i++) x[i]=a[i],y[i]=i;
	for (int i=1;i<=s;i++) wb[i]=0;
	for (int i=1;i<=n;i++) wb[x[y[i]]]++;
	for (int i=1;i<=s;i++) wb[i]+=wb[i-1];
	for (int i=n;i>=1;i--) sa[wb[x[y[i]]]--]=y[i];
	int p =0;
	for (int j=1;p<n;j<<=1){
		p=0;
		for (int i=n-j+1;i<=n;i++) y[++p]=i;
		for (int i=1;i<=n;i++) if (sa[i]>j) y[++p] = sa[i]-j;
		for (int i=1;i<=s;i++) wb[i]=0;
		for (int i=1;i<=n;i++) wb[x[y[i]]]++;
		for (int i=1;i<=s;i++) wb[i]+=wb[i-1];
    	for (int i=n;i>=1;i--) sa[wb[x[y[i]]]--]=y[i];
		swap(x,y);
		p=1;
		x[sa[1]]=1;
		for (int i=2;i<=n;i++){
			x[sa[i]] = (y[sa[i]]==y[sa[i-1]] && y[sa[i]+j]==y[sa[i-1]+j]) ? p : ++p; 
		} 
		s=p;
 	}
	for (int i=1;i<=n;i++) rk[sa[i]]=i;
	h[0]=0;
    for (int i=1;i<=n;i++){
		h[i]=max(h[i-1]-1,0);
		while(i+h[i]<=n && sa[rk[i]-1]+h[i]<=n && a[i+h[i]]==a[sa[rk[i]-1]+h[i]]) h[i]++;
	} 
	for (int i=1;i<=n;i++) height[i]=h[sa[i]];
}
*/
int main()
{
  scanf("%s",a+1);
  n=strlen(a+1);
  for (int i=1;i<=n;i++) a[i+n]=a[i];
  n<<=1;
  getsa();
  for (int i=1;i<=n;i++) if (sa[i]<=n/2) cout<<a[sa[i]+n/2-1];
  cout<<endl;
  
  return 0;
}
  • 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、付费专栏及课程。

余额充值