前言
整体评价
这是一场字典树专场,除了t3这个小模拟之外,1,2,4皆可用字典树搞定。
T4感觉做法挺多的,其实,但是字典树应该效率最高的。
T1. 统计前后缀下标对 I
思路: 模拟
O ( n 2 ) O(n^2) O(n2)全遍历即可
class Solution {
public int countPrefixSuffixPairs(String[] words) {
int res = 0;
for (int i = 0; i < words.length; i++) {
for (int j = 0; j < i; j++) {
if (words[i].startsWith(words[j]) && words[i].endsWith(words[j])) {
res ++;
}
}
}
return res;
}
}
T2. 最长公共前缀的长度
思路: 字典树
这样的时间复杂度为
( m a x a i l e n ( w o r d a i ) ) ∗ ( ∑ b i l e n ( w o r d b i ) ) (max_{ai} len(word_{ai})) * (\sum_{bi} len(word_{bi})) (maxailen(wordai))∗(bi∑len(wordbi))
因为第一组的数字最大为8位数,因此其深度最多为8
public class Solution {
static class Trie {
Trie[] cs = new Trie[26];
void add(String w) {
Trie cur = this;
for (char c: w.toCharArray()) {
int p = c - '0';
if (cur.cs[p] == null) {
cur.cs[p] = new Trie();
}
cur = cur.cs[p];
}
}
int query(String w) {
Trie cur = this;
int res = 0;
for (char c: w.toCharArray()) {
int p = c - '0';
if (cur.cs[p] == null) {
return res;
}
cur = cur.cs[p];
res++;
}
return res;
}
}
public int longestCommonPrefix(int[] arr1, int[] arr2) {
Trie root = new Trie();
for (int v: arr1) {
root.add(String.valueOf(v));
}
int res = 0;
for (int v: arr2) {
int tmp = root.query(String.valueOf(v));
res = Math.max(tmp, res);
}
return res;
}
}
T3. 出现频率最高的素数
思路: 模拟
知识点: 质数筛,质数判定
按题目模拟执行即可
class Solution {
static int[][] dirs = new int[][] {
{-1, 0}, {1, 0}, {0, -1}, {0, 1},
{-1, -1}, {-1, 1}, {1, -1}, {1, 1}
};
List<Integer> create(int[][] mat, int y, int x) {
List<Integer> res = new ArrayList<>();
int n = mat.length, m = mat[0].length;
for (int i = 0; i < dirs.length; i++) {
int v = mat[y][x];
int j = 0;
int ty = y, tx = x;
int base = 10;
do {
ty += dirs[i][0];
tx += dirs[i][1];
if (ty >= 0 && ty < n && tx >= 0 && tx < m) {
v = v + mat[ty][tx] * base;
} else {
break;
}
base *= 10;
res.add(v);
} while(true);
}
return res;
}
boolean isPrime(int v) {
if (v <= 1) return false;
for (int i = 2; i <= v / i; i++) {
if (v % i == 0) return false;
}
return true;
}
public int mostFrequentPrime(int[][] mat) {
Map<Integer, Integer> hash = new HashMap<>();
int n = mat.length, m = mat[0].length;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
List<Integer> tmp = create(mat, i, j);
for (int v: tmp) {
hash.merge(v, 1, Integer::sum);
}
}
}
int fz = -1;
int ans = -1;
for (var kv: hash.entrySet()) {
if (isPrime(kv.getKey()) && kv.getKey() > 10) {
if (fz < kv.getValue() || (fz == kv.getValue() && ans < kv.getKey())) {
fz = kv.getValue();
ans = kv.getKey();
}
}
}
return ans;
}
}
T4. 统计前后缀下标对 II
思路: 字典树+z函数
因为涉及到前后缀一致的问题
所以必然需要引入
- z函数
- stringhash
这类技巧
有涉及到前缀问题,自然就引出字典树
class Solution {
static int[] zFunction(String a) {
char[] s = a.toCharArray();
int n = s.length;
int[] z = new int[n];
z[0] = n;
for (int i = 1, l = 0, r = 0; i < n; ++i) {
if (i <= r && z[i - l] < r - i + 1) {
z[i] = z[i - l];
} else {
z[i] = Math.max(0, r - i + 1);
while (i + z[i] < n && s[z[i]] == s[i + z[i]]) ++z[i];
}
if (i + z[i] - 1 > r) {
l = i;
r = i + z[i] - 1;
}
}
return z;
}
static class Trie {
Trie[] cs = new Trie[26];
int rel;
void add(String w) {
Trie cur = this;
for (char c: w.toCharArray()) {
int p = c - 'a';
if (cur.cs[p] == null) {
cur.cs[p] = new Trie();
}
cur = cur.cs[p];
}
cur.rel++;
}
int count(String w) {
Trie cur = this;
int[] zz = zFunction(w);
int res = 0;
int n = w.length();
for (int i = 0; i < n; i++) {
int p = w.charAt(i) - 'a';
if (cur.cs[p] == null) {
return res;
}
cur = cur.cs[p];
if (zz[n - 1 - i] == i + 1) {
res += cur.rel;
}
}
return res;
}
}
public long countPrefixSuffixPairs(String[] words) {
long res = 0;
Trie root = new Trie();
for (String w: words) {
res += root.count(w);
root.add(w);
}
return res;
}
}