计蒜客 - 新年礼物
计蒜客 新年礼物
新年了,蒜厂 BOSS 要给小蒜头们发新年礼物,新年礼物有很多份,怎么分配这些礼物呢?蒜厂 BOSS 打算让大家玩一个游戏。
蒜头们可以从抽奖箱里抽出 NNN 个字符串,第 iii 个是 xix_ixi,按抽出的顺序从 111 编号。一个蒜头可以得到的礼物个数决定于一个特别的子序列(不要求连续)。当且仅当 xix_ixi 是 xjx_jxj 的前缀,xix_ixi 也是 xjx_jxj 的后缀时,字符串 xix_ixi 和 xjx_jxj(i<ji \lt ji<j) 能在一个子序列中。一个蒜头可以得到的礼物个数符合要求的子序列中最长的那个的长度。
输入格式
第一行输入一个整数 NNN,紧接着输入 NNN 行字符串,每个字符串仅包含小写或大写字母。
输入数据总共少于 2×1062\times 10^62×106 个字符。
输出格式
答案输出在一行,一个整数,表示这个蒜头能得到的礼物个数。
样例 1
5
A
B
AA
BBB
AAA
3
样例 2
5
A
ABA
BBB
ABABA
AAAAAB
3
这道题首先要用拓展 KMP 做一次预处理,记录每个字符串哪些位置的前缀和后缀相同。
private static int[] extendedKMP(String t) {int n = t.length();int[] next = new int[n];next[0] = n;int p = 0;while (p < n - 1 && t.charAt(p) == t.charAt(p + 1)) {p++;}next[1] = p;int k = 1, l;for (int i = 2; i < n; i++) {p = k + next[k] - 1;l = next[i - k];if (i + l - 1 < p) {next[i] = l;} else {int j = p - i + 1;if (j < 0) {j = 0;}while (i + j < n && t.charAt(i + j) == t.charAt(j)) {j++;}next[i] = j;k = i;}}return next;
}
然后我们就可以把字符串插入到前缀树中了。在插入的过程中,始终用 dp[]
数组记录下当前满足条件的最大值,最后只需要遍历 dp[]
数组,找到最大值就是答案。
public static void main(String[] args) {int n = in.nextInt();Trie t = new Trie();int[] dp = new int[n];for (int i = 0; i < n; i++) {String s = in.next();int[] next = extendedKMP(s);t.insert(s, next);}int ans = 0;for (int i = 0; i < n; i++) {ans = Math.max(ans, dp[i]);}System.out.println(ans);
}
在插入的过程中,当满足 isEnd()
这个条件的时候,word
到当前位置为止的前缀是一个已经存在的子串 xix_ixi,所以前缀的性质满足,只需要检查是不是后缀。
这个前缀的长度 len = i + 1
,如果距离最后为 len
的那个位置上,它满足的前缀长度恰好是 len
,那么就是满足的。
if (node.isEnd()) {// 当满足 isEnd() 这个条件的时候,word 到当前位置为止的前缀是一个已经存在的子串 xi// 所以前缀的性质满足,只需要检查是不是后缀// 这个前缀的长度 len = i + 1int len = i + 1;// 如果距离最后为 len 的那个位置上,它满足的前缀长度恰好是 len,那么就是满足的if (next[word.length() - len] == len) {// TODO}
}
对于满足条件的值,我们需要记录下当前满足条件的最大值。
为了编程的方便,我在前缀树的结点中增加了一个字段,用来记录当前结点对应的单词的编号,即对应 dp[]
数组的下标。
private int index;public void setEnd(int index) {end = true;this.index = index;
}public int getIndex() {return index;
}
最后,在上面的 TODO 中填上如下内容:
if (next[word.length() - len] == len) {dp[index] = Math.max(dp[index], dp[node.getIndex()] + 1);
}
注意 dp[]
数组应该被初始化为 1,而不是 0。
import java.util.Arrays;
import java.util.HashMap;
import java.util.Scanner;public class Main {static Scanner in = new Scanner(System.in);private static int[] extendedKMP(String t) {int n = t.length();int[] next = new int[n];next[0] = n;if (n == 1) {// 注意 corner case,否则会出现数组越界return next;}int p = 0;while (p < n - 1 && t.charAt(p) == t.charAt(p + 1)) {p++;}next[1] = p;int k = 1, l;for (int i = 2; i < n; i++) {p = k + next[k] - 1;l = next[i - k];if (i + l - 1 < p) {next[i] = l;} else {int j = p - i + 1;if (j < 0) {j = 0;}while (i + j < n && t.charAt(i + j) == t.charAt(j)) {j++;}next[i] = j;k = i;}}return next;}public static void main(String[] args) {int n = in.nextInt();Trie t = new Trie();int[] dp = new int[n];// 赋初值为 1,因为至少自己是满足条件的Arrays.fill(dp, 1);for (int i = 0; i < n; i++) {String s = in.next();int[] next = extendedKMP(s);t.insert(s, next, i, dp);}int ans = 0;for (int i = 0; i < n; i++) {ans = Math.max(ans, dp[i]);}System.out.println(ans);}}class Trie {private TrieNode root;/*** Initialize your data structure here.*/public Trie() {root = new TrieNode();}/*** Inserts a word into the trie.*/public void insert(String word, int[] next, int index, int[] dp) {TrieNode node = root;for (int i = 0; i < word.length(); i++) {char c = word.charAt(i);if (!node.contains(c)) {node.put(c);}node = node.get(c);if (node.isEnd()) {// 当满足 isEnd() 这个条件的时候,word 到当前位置为止的前缀是一个已经存在的子串 xi// 所以前缀的性质满足,只需要检查是不是后缀// 这个前缀的长度 len = i + 1int len = i + 1;// 如果距离最后为 len 的那个位置上,它满足的前缀长度恰好是 len,那么就是满足的if (next[word.length() - len] == len) {dp[index] = Math.max(dp[index], dp[node.getIndex()] + 1);}}}node.setEnd(index);}
}class TrieNode {private HashMap<Character, TrieNode> links;private boolean end;private int index;public TrieNode() {links = new HashMap<>();end = false;}public boolean isEnd() {return end;}public void setEnd(int index) {end = true;this.index = index;}public int getIndex() {return index;}public TrieNode get(char c) {return links.get(c);}public void put(char c) {links.put(c, new TrieNode());}public boolean contains(char c) {return links.containsKey(c);}}
欢迎关注我的个人博客以优秀文章:凝神长老和他的朋友们(https://www.jxtxzzw.com)
也欢迎关注我的其他平台:知乎( https://s.zzw.ink/zhihu )、知乎专栏( https://s.zzw.ink/zhuanlan )、哔哩哔哩( https://s.zzw.ink/blbl )、微信公众号( 凝神长老和他的朋友们 )
计蒜客 - 新年礼物相关推荐
- 计蒜客 新年礼物 (trie+exkmp+dp)
刚开始拿上这道题,没有一点思路,看了提示,说kmp处理前后缀,才恍然大悟,加上dp处理结果,妙哉. #include<bits/stdc++.h>using namespace std;c ...
- 计蒜客 挑战难题 爬楼梯
计蒜客 挑战难题 爬楼梯 假设你现在正在爬楼梯,楼梯有n级.每次你只能爬1级或者2级,那么你有多少种方法爬到楼梯的顶部? 格式: 第一行输入一个数n(n<=50),代表楼梯的级数. 接下来一行输 ...
- 无脑博士的试管们java_计蒜客 无脑博士和他的试管们
标签: 无脑博士有三个容量分别是A,B,C升的试管,A,B,C分别是三个从1到20的整数,最初,A和B试管都是空的,而C试管是装满硫酸铜溶液的.有时,无脑博士把硫酸铜溶液从一个试管倒到另一个试管中,直 ...
- 最短路(遍历边)计蒜客—DD坐地铁
题目: C 城有 n 个站点, m 条双向地铁,每条地铁有一个 companyicompany_icompanyi表示它的公司,如果连续乘坐同一家公司的地铁只要花 1元钱就好.DD现在想出门找萨摩耶 ...
- K - Triangle 计蒜客 - 42405
K - Triangle 计蒜客 - 42405 题意: 给你一个三角形的三点,再给你三角形边上一个点,让你求另一个点(也要在三角形上),使得平分三角形的面积 题解: 计算几何 三角形的三边ab,ac ...
- H - Prince and Princess 计蒜客 - 42402
H - Prince and Princess 计蒜客 - 42402 题意: 你现在要寻找公主,有三种人,第一种是说真话的人(至少为1,因为公主是说真话的人),第二种人是只会说假话的,第三种是胡说八 ...
- C - Digital Path 计蒜客 - 42397 05-29
C - Digital Path 计蒜客 - 42397 题意: 题意就是给出一个n ∗ m的数字矩阵每个矩阵元素之间只能上下左右走,而且下一个位置必须比当前位置的数字大1,入口和出口必须数边缘元素, ...
- 计蒜客可以做计算机编程吗,如果你的编程能力不足以支撑你成为工程师的野心,不妨到计蒜客上学学看...
在人才招聘领域存在这样一个怪圈,高校每年都说是最难就业年.人才过剩,而对于企业HR来说永远都招不到称心如意的人才.这个怪圈在"计蒜客"创始人俞昊然看来,主要是因为当今高校的教学资源 ...
- 计蒜客 - T1012 A*B问题
计蒜客 - T1012 A*B问题 相信你已经学会 A+B 问题了,那么问题又来了- 输入两个正整数 A 和 B ,求 A×B. 输入格式 一行,包含两个正整数 A 和 B,中间用单个空格隔开. 1≤ ...
最新文章
- c语言合法常量2.57e03,[单选] 目前杭州共有世界遗产()项。
- 东京奥运会73枚动态图标刷爆朋友圈,中国网友怒赞:不愧是设计大国!
- 修改ubuntu崩溃转储的设置
- 解决非controller使用@Autowired注解注入报错为java.lang.NullPointerException问题
- 数据结构之堆与优先队列
- 解决Android拍照保存在系统相册不显示的问题
- 对Map集合排序,先对value降序,value相同的情况下,key升序
- 哪本最具影响力的书,是每个程序员都应该读的?
- 高中计算机会考vb试题,高中信息技术考试vb程序题及答案
- Ribbon的基本应用
- 骨架屏Skeleton Screen
- android imageview 锯齿,android – 如何在启用消除锯齿的情况下旋转drawable
- js rsa java解密_RSA使用js加密,使用java解密
- 送给年轻而不服输的你
- 后缀数组水水水水水水题
- 电脑右下角WIFI图标上出现的长方形白框
- xshell连接linux系统
- 【JAVA】Supplier
- 库存商品出入库方式分析
- C# 验厂计时考勤工资系统