modify code

master
algorithmzuo 2 years ago
parent 4d29825c5a
commit abb9862872

@ -0,0 +1,151 @@
package class_2023_05_5_week;
// 来自字节
// 给定一个n*m的二维矩阵每个位置都是字符
// U、D、L、R表示传送带的位置会被传送到 : 上、下、左、右
// . 、O分别表示空地、目标一定只有一个目标点
// 可以在空地上选择上、下、左、右四个方向的一个
// 到达传送带的点会被强制移动到其指向的下一个位置
// 如果越界直接结束返回有几个点可以到达O点
public class Code01_WhereCanReachNumber {
// 暴力方法
// 为了测试
public static int number1(char[][] map) {
int ans = 0;
int n = map.length;
int m = map[0].length;
boolean[][] visited = new boolean[n][m];
for (int i = 0; i < map.length; i++) {
for (int j = 0; j < map[0].length; j++) {
if (dfs(map, i, j, visited)) {
ans++;
}
}
}
return ans;
}
// 暴力方法
// 为了测试
public static boolean dfs(char[][] map, int i, int j, boolean[][] visited) {
if (i < 0 || i == map.length || j < 0 || j == map[0].length || visited[i][j]) {
return false;
}
visited[i][j] = true;
boolean ans = false;
if (map[i][j] == 'O') {
ans = true;
} else {
if (map[i][j] == 'U') {
ans = dfs(map, i - 1, j, visited);
} else if (map[i][j] == 'D') {
ans = dfs(map, i + 1, j, visited);
} else if (map[i][j] == 'L') {
ans = dfs(map, i, j - 1, visited);
} else if (map[i][j] == 'R') {
ans = dfs(map, i, j + 1, visited);
} else {
ans = dfs(map, i - 1, j, visited) || dfs(map, i + 1, j, visited) || dfs(map, i, j - 1, visited)
|| dfs(map, i, j + 1, visited);
}
}
visited[i][j] = false;
return ans;
}
// 正式方法
// 时间复杂度O(n*m)
public static int number2(char[][] map) {
int n = map.length;
int m = map[0].length;
boolean[][] visited = new boolean[n][m];
// queue[i] = {行坐标、列坐标}
int[][] queue = new int[n * m][2];
int l = 0;
int r = 0;
int ans = 0;
// O在哪目的地
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (map[i][j] == 'O') {
visited[i][j] = true;
queue[r][0] = i;
queue[r++][1] = j;
break;
}
}
}
// [] [] [] [] [] ...
// l ...... r
while (l < r) { // 队列里还有位置!
ans++;
int[] cur = queue[l++];
int row = cur[0];
int col = cur[1];
if (row - 1 >= 0 && !visited[row - 1][col] && (map[row - 1][col] == 'D' || map[row - 1][col] == '.')) {
visited[row - 1][col] = true;
queue[r][0] = row - 1;
queue[r++][1] = col;
}
if (row + 1 < n && !visited[row + 1][col] && (map[row + 1][col] == 'U' || map[row + 1][col] == '.')) {
visited[row + 1][col] = true;
queue[r][0] = row + 1;
queue[r++][1] = col;
}
if (col - 1 >= 0 && !visited[row][col - 1] && (map[row][col - 1] == 'R' || map[row][col - 1] == '.')) {
visited[row][col - 1] = true;
queue[r][0] = row;
queue[r++][1] = col - 1;
}
if (col + 1 < m && !visited[row][col + 1] && (map[row][col + 1] == 'L' || map[row][col + 1] == '.')) {
visited[row][col + 1] = true;
queue[r][0] = row;
queue[r++][1] = col + 1;
}
}
return ans;
}
// 生成随机地图
// 为了测试
public static char[][] genarateRandomMap(int n, int m) {
char[][] map = new char[n][m];
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
int r = (int) (Math.random() * 5);
if (r == 0) {
map[i][j] = 'U';
} else if (r == 1) {
map[i][j] = 'D';
} else if (r == 2) {
map[i][j] = 'L';
} else if (r == 3) {
map[i][j] = 'R';
} else {
map[i][j] = '.';
}
}
}
map[(int) (Math.random() * n)][(int) (Math.random() * m)] = 'O';
return map;
}
// 为了测试
public static void main(String[] args) {
int n = 10;
int m = 10;
int testTimes = 10000;
System.out.println("测试开始");
for (int i = 0; i < testTimes; i++) {
char[][] map = genarateRandomMap(n, m);
int ans1 = number1(map);
int ans2 = number2(map);
if (ans1 != ans2) {
System.out.println("出错了!");
}
}
System.out.println("测试结束");
}
}

@ -0,0 +1,106 @@
package class_2023_05_5_week;
// 来自学员问题
// 沿街有一排连续的房屋。每间房屋内都藏有一定的现金
// 现在有一位小偷计划从这些房屋中窃取现金
// 由于相邻的房屋装有相互连通的防盗系统,所以小偷 不会窃取相邻的房屋
// 小偷的 窃取能力 定义为他在窃取过程中能从单间房屋中窃取的 最大金额
// 给你一个整数数组 nums 表示每间房屋存放的现金金额
// 形式上,从左起第 i 间房屋中放有 nums[i] 美元
// 另给你一个整数 k ,表示窃贼将会窃取的最少房屋数
// 小偷一定要要窃取至少 k 间房屋,返回小偷的 最小 窃取能力
// 测试链接 : https://leetcode.cn/problems/house-robber-iv/
public class Code02_HouseRobberIV {
// https://leetcode.cn/problems/house-robber/
public static int rob(int[] arr) {
int n = arr.length;
if (n == 1) {
return arr[0];
}
if (n == 2) {
return Math.max(arr[0], arr[1]);
}
// dp[0]: lastLast
int lastLast = arr[0];
// dp[1] : last
int last = Math.max(arr[0], arr[1]);
for (int i = 2; i < n; i++) {
// lastLast : dp[i-2]
// last : dp[i-1];
// cur
int p1 = last;
int p2 = arr[i] + lastLast;
int cur = Math.max(p1, p2);
lastLast = last;
last = cur;
}
return last;
}
public static int minCapability(int[] nums, int k) {
int l = 1;
int r = 0;
for (int num : nums) {
r = Math.max(num, r);
}
// 1 ~ max
int m, ans = 0;
// 二分答案法
while (l <= r) {
// m是当前盗贼的能力
// 很明显盗贼能力越大,能盗窃房屋数量的最大值只可能不变、或者变多,不可能变少
m = (l + r) / 2;
if (robber(nums, m) >= k) {
// 如果盗贼当前的能力下盗窃房屋数量的最大值超过了k
// 说明这个能力达标,但是希望看看左侧范围上,还有没有依然能达标的能力
// 所以,记录答案,去左侧二分
ans = m;
r = m - 1;
} else {
// 如果盗贼当前的能力下盗窃房屋数量的最大值小于k
// 说明这个能力不达标,只能去右侧范围上看看有没有达标的能力
// 所以,不记录答案,去右侧二分
l = m + 1;
}
}
return ans;
}
// 盗贼能力为ability时返回盗贼最多能窃取多少间房屋
// 注意不能窃取相邻房屋
public static int robber(int[] nums, int ability) {
// lastLast表示0...0范围上,盗贼最多能窃取多少间房屋
int lastLast = nums[0] <= ability ? 1 : 0;
int n = nums.length;
if (n == 1) {
return lastLast;
}
// last表示0...1范围上,盗贼最多能窃取多少间房屋
int last = (nums[0] <= ability || nums[1] <= ability) ? 1 : 0;
int ans = Math.max(lastLast, last);
for (int i = 2; i < n; i++) {
// 可能性1 : 就是不盗窃i号房屋
// 那么0...i-1范围上怎么盗窃得到的最优解(last)就是此时0....i范围上的最优解
int p1 = last;
// 可能性2 : 就是盗窃i号房屋
// 先决条件 : i号房屋的财产 <= 当前盗贼的能力这个前提必须具备才能盗窃i号房屋
// 如果先决条件具备那么盗窃完i号房屋之前就只能在0...i-2范围上去选房屋了(lastLast)
int p2 = 0;
if (nums[i] <= ability) {
p2 = lastLast + 1;
}
// 两种可能性取最优
int cur = Math.max(p1, p2);
// 记录答案
ans = Math.max(ans, cur);
// 不要忘了更新lastLast、last
// lastLast始终表示: 在0...i-2范围上去选房屋了怎么盗窃数量最多
// last始终表示: 在0...i-1范围上去选房屋了怎么盗窃数量最多
lastLast = last;
last = cur;
}
return ans;
}
}

@ -0,0 +1,105 @@
package class_2023_05_5_week;
// 来自华为OD
// 如果n = 1打印
// 1***
// 如果n = 2打印
// 1***
// 3*** 2***
// 如果n = 3打印
// 1***
// 3*** 2***
// 4*** 5*** 6***
// 如果n = 4打印
// 1***
// 3*** 2***
// 4*** 5*** 6***
// 10** 9*** 8*** 7***
// 输入一个数n表示有多少行从1开始输出
// 奇数行输出奇数个数,奇数行正序,偶数行输出偶数个数,偶数行逆序
// 每个数后面加*补满四位中间空4个第n行顶格输出
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StreamTokenizer;
import java.util.Arrays;
// 直接在插件里运行
// 根据约定不要让n超过140
// 因为n=140时最后的数字是9870
// 如果n=141那样最后的数字是10011这样就会超过4位
// 而超过4位之后的样子你并没有说所以n最大是140不要超过
public class Code03_PrintZigZagWithStar {
public static int MAXN = 100001;
public static char[] space = new char[MAXN];
public static int n, m;
public static void main(String[] args) throws IOException {
// 提交时,把这一句删掉
// 就是下面这一句 : System.out.println("提醒请输入n : ")
// 这是为了给你测试才写的,提交时候删掉这一句提醒
System.out.println("提醒请输入n : ");
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StreamTokenizer in = new StreamTokenizer(br);
PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
while (in.nextToken() != StreamTokenizer.TT_EOF) {
n = (int) in.nval;
m = n * 8;
Arrays.fill(space, 0, m, ' ');
boolean from = true;
for (int i = 1, j = 1; i <= n; j += i, i++) {
fill(from, j, i);
for (int k = 0; k < m - 4; k++) {
out.print(space[k]);
}
out.println();
from = !from;
}
out.flush();
}
}
public static void fill(boolean from, int start, int number) {
if (from) {
for (int i = m - number * 8, j = 1; j <= number; i += 8, start++, j++) {
insert(start, i);
}
} else {
for (int i = m - 8, j = 1; j <= number; i -= 8, start++, j++) {
insert(start, i);
}
}
}
// 135, i...
// 1 3 5 *
// i
public static void insert(int cur, int i) {
// X
// i +1 +2 +3 +4
int end = i + 4;
int bit = cur > 999 ? 4 : (cur > 99 ? 3 : (cur > 9) ? 2 : 1);
// 135 bit = 3
// offset = 100
// (135 / 100) % 10 = 1
// (135 / 10) % 10 = 3
// (135 / 1) % 10 = 5
// 4567 bit = 4
// offset = 1000
// (cur / offset) % 10 -> 提取每一位的数字
int offset = bit == 4 ? 1000 : (bit == 3 ? 100 : (bit == 2 ? 10 : 1));
while (offset > 0) {
space[i++] = (char) (((cur / offset) % 10) + '0');
offset /= 10;
}
while (i < end) {
space[i++] = '*';
}
}
}

@ -0,0 +1,103 @@
package class_2023_05_5_week;
// 字符串哈希原理和实现
public class Code04_StringHash {
// 暴力方法
// 为了验证
public static boolean rightCheck(String str, int l1, int l2, int len) {
if (l1 + len > str.length() || l2 + len > str.length()) {
return false;
}
if (l1 == l2) {
return true;
}
return str.substring(l1, l1 + len).equals(str.substring(l2, l2 + len));
}
// 哈希方法检测
public static int MAXN = 100005;
public static long[] pow = new long[MAXN];
public static long[] hash = new long[MAXN];
public static int base = 499;
public static void build(String str, int n) {
pow[0] = 1;
for (int j = 1; j < n; j++) {
pow[j] = pow[j - 1] * base;
}
// a -> 1
// b -> 2
// c -> 3
// z -> 26
// 前缀和的哈希值
hash[0] = str.charAt(0) - 'a' + 1;
for (int j = 1; j < n; j++) {
hash[j] = hash[j - 1] * base + str.charAt(j) - 'a' + 1;
}
}
public static boolean hashCheck(int n, int l1, int l2, int len) {
int r1 = l1 + len - 1;
int r2 = l2 + len - 1;
if (r1 >= n || r2 >= n) {
return false;
}
return hash(l1, r1) == hash(l2, r2);
}
// s[l...r]
public static long hash(int l, int r) {
// hash[0] : s[0...0]
// hash[5] : s[0...5]
// hash[i] : s[0...i]
long ans = hash[r];
ans -= l == 0 ? 0 : (hash[l - 1] * pow[r - l + 1]);
return ans;
}
// 为了测试
public static String randomString(int len, int v) {
char[] str = new char[len];
for (int i = 0; i < len; i++) {
str[i] = (char) ('a' + (int) (Math.random() * v));
}
return String.valueOf(str);
}
// 为了测试
public static void main(String[] args) {
String test = "abcabcabcabcabcabcabcabc";
int size = test.length();
build(test, size);
System.out.println(hashCheck(size, 6, 15, 3));
System.out.println("测试开始");
int N = 10000;
int V = 3;
int testTeams = 100;
int testTimes = 5000;
int LEN = 6;
for (int i = 0; i < testTeams; i++) {
int n = (int) (Math.random() * N) + 1;
String str = randomString(n, V);
build(str, n);
for (int k = 0; k <= testTimes; k++) {
int l1 = (int) (Math.random() * n);
int l2 = (int) (Math.random() * n);
int len = (int) (Math.random() * LEN) + 1;
boolean ans1 = rightCheck(str, l1, l2, len);
boolean ans2 = hashCheck(n, l1, l2, len);
if (ans1 != ans2) {
System.out.println("出错了!");
break;
}
}
}
System.out.println("测试结束");
}
}

@ -0,0 +1,170 @@
package class_2023_05_5_week;
// 字符串哈希+二分的例题
// 给定长为 n 的源串 s以及长度为 m 的模式串 p
// 要求查找源串中有多少子串与模式串匹配
// s' 与 s 匹配,当且仅当 s' 与 s 长度相同,且最多有 k 个位置字符不同
// 其中 1 <= n, m <= 10^60 <= k <= 5
public class Code05_DiffLessKMatchNumber {
// 暴力方法
// 为了测试
public static int howMany1(String str1, String str2, int k) {
int n = str1.length();
int m = str2.length();
if (n < m) {
return 0;
}
char[] s = str1.toCharArray();
char[] p = str2.toCharArray();
int ans = 0;
for (int i = 0; i <= n - m; i++) {
if (diffLessK1(s, i, p, k, m)) {
ans++;
}
}
return ans;
}
// s[i...]和p有多少字符不一样
public static boolean diffLessK1(char[] s, int i, char[] p, int k, int m) {
int diff = 0;
for (int j = 0; j < m; j++) {
if (s[i + j] != p[j]) {
diff++;
}
}
return diff <= k;
}
// 正式方法
// 时间复杂度O(N*K*logM)
public static int MAXN = 100001;
public static int base = 1000000007;
public static long[] pow = new long[MAXN];
static {
pow[0] = 1;
for (int j = 1; j < MAXN; j++) {
pow[j] = pow[j - 1] * base;
}
}
public static long[] hashs = new long[MAXN];
public static long[] hashp = new long[MAXN];
public static void buildHash(char[] s, int n, char[] p, int m) {
hashs[0] = s[0] - 'a' + 1;
for (int j = 1; j < n; j++) {
hashs[j] = hashs[j - 1] * base + s[j] - 'a' + 1;
}
hashp[0] = p[0] - 'a' + 1;
for (int j = 1; j < m; j++) {
hashp[j] = hashp[j - 1] * base + p[j] - 'a' + 1;
}
}
public static int howMany2(String str1, String str2, int k) {
int n = str1.length();
int m = str2.length();
if (n < m) {
return 0;
}
char[] s = str1.toCharArray();
char[] p = str2.toCharArray();
buildHash(s, n, p, m);
int ans = 0;
for (int i = 0; i <= n - m; i++) {
if (diffLessK2(i, i + m - 1, 0, m - 1, k)) {
ans++;
}
}
return ans;
}
// s[l1......r1]
// p[l2......r2]
// 这两段一定等长!
// 返回,这两段上字符不一样的位置,是不是 <= k个的
public static boolean diffLessK2(int l1, int r1, int l2, int r2, int k) {
int diff = 0;
// l1 <= r1 : 目前还剩下一些字符串
// diff <= k: 不一样的数量没有超!
while (l1 <= r1 && diff <= k) {
// 二分 : s[l1.......] p[l2........] 最长的相等长度!
// s : abcdefgiii....
// p : abcedfgihh....
int l = 1;
int r = r1 - l1 + 1;
int m, len = 0;
while (l <= r) {
m = (l + r) / 2;
// ok(l1, l2, m)
// s[l1...数m长度(包括l1)]
// 是不是等于
// p[l2...数m长度(包括l2)]
if (ok(l1, l2, m)) {
len = m;
l = m + 1;
} else {
r = m - 1;
}
}
if (l1 + len <= r1) {
diff++;
}
l1 += len + 1;
l2 += len + 1;
}
return diff <= k;
}
public static boolean ok(int l1, int l2, int len) {
return hash(hashs, l1, l1 + len - 1) == hash(hashp, l2, l2 + len - 1);
}
public static long hash(long[] hash, int l, int r) {
long ans = hash[r];
ans -= l == 0 ? 0 : (hash[l - 1] * pow[r - l + 1]);
return ans;
}
// 为了测试
// 生成随机子串
public static String randomString(int len, int range) {
char[] str = new char[len];
for (int i = 0; i < len; i++) {
str[i] = (char) ((int) (Math.random() * range) + 'a');
}
return String.valueOf(str);
}
// 为了测试
public static void main(String[] args) {
int N = 100;
int M = 50;
int K = 10;
// a b c
// R =4 abcd
int R = 3;
int testTimes = 10000;
System.out.println("测试开始");
for (int i = 0; i < testTimes; i++) {
int n = (int) (Math.random() * N) + 1;
int m = (int) (Math.random() * M) + 1;
int k = (int) (Math.random() * K);
String str1 = randomString(n, R);
String str2 = randomString(m, R);
int ans1 = howMany1(str1, str2, k);
int ans2 = howMany2(str1, str2, k);
if (ans1 != ans2) {
System.out.println("出错了!");
}
}
System.out.println("测试结束");
}
}

@ -0,0 +1,105 @@
package class_2023_05_5_week;
// 正方形矩阵哈希实现
// 二维哈希只适用于正方形的情况
// 如果想支持普通矩阵,需要更复杂度的过程,这里不做展开
public class Code06_TwoDimensionalHash {
public static int MAXN = 1001;
public static long[] powr = new long[MAXN];
public static long[] powc = new long[MAXN];
public static long[][] sum = new long[MAXN][MAXN];
public static int baser = 491;
public static int basec = 499;
public static void buildHash(int[][] arr) {
int n = arr.length - 1;
int m = arr[0].length - 1;
powr[0] = 1;
powc[0] = 1;
for (int i = 1; i <= n; i++) {
powr[i] = (powr[i - 1] * baser);
}
for (int i = 1; i <= m; i++) {
powc[i] = (powc[i - 1] * basec);
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
// 左 * basec + arr[i][j]
sum[i][j] = sum[i][j - 1] * basec + arr[i][j];
}
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
// 上 * baser
sum[i][j] += sum[i - 1][j] * baser;
}
}
}
public static int[][] randomArray(int n, int m) {
int[][] arr = new int[n + 1][m + 1];
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
arr[i][j] = Math.random() < 0.5 ? 5 : 6;
}
}
return arr;
}
public static void main(String[] args) {
int n = 100;
int m = 100;
int[][] arr = randomArray(n, m);
buildHash(arr);
int testTimes = 50000;
int len = 5;
System.out.println("测试开始");
for (int i = 0; i < testTimes; i++) {
int a = (int) (Math.random() * 90) + 1;
int b = (int) (Math.random() * 90) + 1;
int c = (int) (Math.random() * 90) + 1;
int d = (int) (Math.random() * 90) + 1;
int sizer = (int) (Math.random() * len) + 1;
int sizec = sizer; // 如果矩阵是正方形,完全可以使用
// sizec = (int) (Math.random() * len) + 1; // 如果矩阵不是正方形,不能用!会报错!
boolean ans1 = rightCheck(arr, a, b, c, d, sizer, sizec);
boolean ans2 = hashCheck(a, b, c, d, sizer, sizec);
if (ans1 != ans2) {
System.out.println("出错了!");
}
}
System.out.println("测试结束");
}
// 当前矩阵,必须是正方形!
// 左上点(a,b)
// 右下点(c,d)
public static long hash(int a, int b, int c, int d) {
return sum[c][d]
- sum[a - 1][d] * powr[c - a + 1]
- sum[c][b - 1] * powc[d - b + 1]
+ sum[a - 1][b - 1] * powr[d - b + 1] * powc[c - a + 1];
}
public static boolean hashCheck(int a, int b, int c, int d, int lenr, int lenc) {
return hash(a, b, a + lenr - 1, b + lenc - 1) == hash(c, d, c + lenr - 1, d + lenc - 1);
}
public static boolean rightCheck(int[][] arr, int a, int b, int c, int d, int lenr, int lenc) {
for (int i = a, j = c; i < a + lenr; i++, j++) {
for (int p = b, q = d; p < b + lenc; p++, q++) {
if (arr[i][p] != arr[j][q]) {
return false;
}
}
}
return true;
}
}

@ -0,0 +1,169 @@
package class_2023_05_5_week;
// 二维哈希的解法
// 二维哈希只适用于正方形的情况
// 如果想支持普通矩阵,需要更复杂度的过程,这里不做展开
// 来自学员问题
// 如果一个正方形矩阵上下对称并且左右对称,对称的意思是互为镜像
// 那么称这个正方形矩阵叫做神奇矩阵
// 比如 :
// 1 5 5 1
// 6 3 3 6
// 6 3 3 6
// 1 5 5 1
// 这个正方形矩阵就是神奇矩阵
// 给定一个大矩阵n*m返回其中神奇矩阵的数目
// 1 <= n,m <= 1000
// 测试链接 : https://www.luogu.com.cn/problem/P2601
// 请同学们务必参考如下代码中关于输入、输出的处理
// 这是输入输出处理效率很高的写法
// 提交以下的code提交时请把类名改成"Main"
// 这个题所有用户的java提交里我是唯一通过的
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StreamTokenizer;
public class Code07_NumberOfPalindromicSquares {
public static int MAXN = 1001;
public static int baser = 491;
public static int basec = 499;
public static long[] powr = new long[MAXN];
public static long[] powc = new long[MAXN];
static {
powr[0] = 1;
powc[0] = 1;
for (int i = 1; i < MAXN; i++) {
powr[i] = (powr[i - 1] * baser);
}
for (int i = 1; i < MAXN; i++) {
powc[i] = (powc[i - 1] * basec);
}
}
// 原始
public static int[][] arr1 = new int[MAXN][MAXN];
// 上下翻转
public static int[][] arr2 = new int[MAXN][MAXN];
// 左右翻转
public static int[][] arr3 = new int[MAXN][MAXN];
// 各自哈希
public static long[][] sum1 = new long[MAXN][MAXN];
public static long[][] sum2 = new long[MAXN][MAXN];
public static long[][] sum3 = new long[MAXN][MAXN];
public static int n, m;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StreamTokenizer in = new StreamTokenizer(br);
PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
while (in.nextToken() != StreamTokenizer.TT_EOF) {
n = (int) in.nval;
in.nextToken();
m = (int) in.nval;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
in.nextToken();
arr1[i][j] = (int) in.nval;
arr2[n - i + 1][j] = arr1[i][j];
arr3[i][m - j + 1] = arr1[i][j];
}
}
buildHash(arr1, sum1);
buildHash(arr2, sum2);
buildHash(arr3, sum3);
out.println(number());
out.flush();
}
}
public static void buildHash(int[][] arr, long[][] sum) {
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
sum[i][j] = sum[i][j - 1] * basec + arr[i][j];
}
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
sum[i][j] += sum[i - 1][j] * baser;
}
}
}
public static long hash(long[][] sum, int a, int b, int c, int d) {
long ans =
sum[c][d]
- sum[a - 1][d] * powr[c - a + 1]
- sum[c][b - 1] * powc[d - b + 1]
+ sum[a - 1][b - 1] * powr[d - b + 1] * powc[c - a + 1];
return ans;
}
public static int number() {
int ans = 0;
// 奇数长度,实点做中心
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
int l = 1;
int r = Math.min(Math.min(i, n - i + 1), Math.min(j, m - j + 1));
int m, find = 1;
while (l <= r) {
m = (l + r) / 2;
if (ok(i - m + 1, j - m + 1, i + m - 1, j + m - 1)) {
find = m;
l = m + 1;
} else {
r = m - 1;
}
}
ans += find;
}
}
// 偶数长度
// 虚点做中心
for (int i = 1; i < n; i++) {
for (int j = 1; j < m; j++) {
// 左上角点为代表
int l = 1;
int r = Math.min(Math.min(i, j), Math.min(n - i, m - j));
int m, find = 0;
while (l <= r) {
m = (l + r) / 2;
if (ok(i - m + 1, j - m + 1, i + m, j + m)) {
find = m;
l = m + 1;
} else {
r = m - 1;
}
}
ans += find;
}
}
return ans;
}
public static boolean ok(int a, int b, int c, int d) {
if (a == c) {
return true;
}
long h1 = hash(sum1, a, b, c, d);
long h2 = hash(sum2, n - c + 1, b, n - a + 1, d);
long h3 = hash(sum3, a, m - d + 1, c, m - b + 1);
return h1 == h2 && h1 == h3;
}
}

@ -0,0 +1,200 @@
package class_2023_05_5_week;
// manacher算法的解法
// 来自学员问题
// 如果一个正方形矩阵上下对称并且左右对称,对称的意思是互为镜像
// 那么称这个正方形矩阵叫做神奇矩阵
// 比如 :
// 1 5 5 1
// 6 3 3 6
// 6 3 3 6
// 1 5 5 1
// 这个正方形矩阵就是神奇矩阵
// 给定一个大矩阵n*m返回其中神奇矩阵的数目
// 1 <= n,m <= 1000
// 测试链接 : https://www.luogu.com.cn/problem/P2601
// 请同学们务必参考如下代码中关于输入、输出的处理
// 这是输入输出处理效率很高的写法
// 提交以下的code提交时请把类名改成"Main"
// 如果提交不是都通过,就多提交两次
// 洛谷对java不友好有可以都通过的时候
// 这个题所有用户的java提交里我是唯一通过的
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StreamTokenizer;
public class Code08_NumberOfPalindromicSquares {
public static int MAXN = 1001;
public static int[] log2 = new int[(MAXN << 1 | 1) + 1];
static {
for (int k = 0, j = 1; j <= (MAXN << 1 | 1); j++) {
if (1 << (k + 1) <= j) {
k++;
}
log2[j] = k;
}
}
// 扩充
// 1 1
// 1 1
//
// 0 0 0 0 0
// 0 1 0 1 0
// 0 0 0 0 0
// 0 1 0 1 0
// 0 0 0 0 0
public static int[][] arr = new int[MAXN << 1 | 1][MAXN << 1 | 1];
// 每个点在行方向上,回文半径多大
public static int[][] rp = new int[MAXN << 1 | 1][MAXN << 1 | 1];
// 每个点在列方向上,回文半径多大
public static int[][] cp = new int[MAXN << 1 | 1][MAXN << 1 | 1];
// enlarge[i][j] : 以ij点做中心最多扩多大还是神奇矩阵
public static int[][] enlarge = new int[MAXN << 1 | 1][MAXN << 1 | 1];
public static int[][] rmq = new int[MAXN << 1 | 1][13];
public static int[] s = new int[MAXN << 1 | 1];
public static int[] p = new int[MAXN << 1 | 1];
public static int n, m;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StreamTokenizer in = new StreamTokenizer(br);
PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
while (in.nextToken() != StreamTokenizer.TT_EOF) {
n = (int) in.nval;
in.nextToken();
m = (int) in.nval;
for (int i = 0, r = 1; i < n; i++, r += 2) {
for (int j = 0, c = 1; j < m; j++, c += 2) {
in.nextToken();
arr[r][c] = (int) in.nval;
}
}
n = n * 2 + 1;
m = m * 2 + 1;
out.println(number());
out.flush();
}
}
public static int number() {
for (int row = 0; row < n; row++) {
manacher(row, 0, 0, 1);
}
for (int col = 0; col < m; col++) {
manacher(0, col, 1, 0);
}
for (int row = 1; row < n - 1; row++) {
rowRmq(row);
for (int col = 1; col < m - 1; col++) {
int l = 1;
int r = Math.min(Math.min(row + 1, n - row), Math.min(col + 1, m - col));
int m, find = 1;
while (l <= r) {
m = (l + r) / 2;
if (query(col - m + 1, col + m - 1) >= m) {
find = m;
l = m + 1;
} else {
r = m - 1;
}
}
enlarge[row][col] = find;
}
}
for (int col = 1; col < m - 1; col++) {
colRmq(col);
for (int row = 1; row < n - 1; row++) {
int l = 1;
int r = Math.min(Math.min(row + 1, n - row), Math.min(col + 1, m - col));
int m, find = 1;
while (l <= r) {
m = (l + r) / 2;
if (query(row - m + 1, row + m - 1) >= m) {
find = m;
l = m + 1;
} else {
r = m - 1;
}
}
enlarge[row][col] = Math.min(enlarge[row][col], find);
}
}
int ans = 0;
for (int row = 1; row < n - 1; row += 2) {
for (int col = 1; col < m - 1; col += 2) {
ans += enlarge[row][col] / 2;
}
}
for (int row = 2; row < n - 1; row += 2) {
for (int col = 2; col < m - 1; col += 2) {
ans += (enlarge[row][col] - 1) / 2;
}
}
return ans;
}
public static void manacher(int row, int col, int radd, int cadd) {
int limit = 0;
for (int r = row, c = col; r < n && c < m; r += radd, c += cadd) {
s[limit++] = arr[r][c];
}
int C = -1;
int R = -1;
for (int i = 0; i < limit; i++) {
p[i] = R > i ? Math.min(p[2 * C - i], R - i) : 1;
while (i + p[i] < limit && i - p[i] > -1 && s[i + p[i]] == s[i - p[i]]) {
p[i]++;
}
if (i + p[i] > R) {
R = i + p[i];
C = i;
}
}
int[][] fill = cadd == 1 ? rp : cp;
for (int i = 0, r = row, c = col; i < limit; i++, r += radd, c += cadd) {
fill[r][c] = p[i];
}
}
public static void rowRmq(int row) {
for (int i = 0; i < m; i++) {
rmq[i][0] = cp[row][i];
}
for (int j = 1; (1 << j) <= m; j++) {
for (int i = 0; i + (1 << j) - 1 < m; i++) {
rmq[i][j] = Math.min(rmq[i][j - 1], rmq[i + (1 << (j - 1))][j - 1]);
}
}
}
public static void colRmq(int col) {
for (int i = 0; i < n; i++) {
rmq[i][0] = rp[i][col];
}
for (int j = 1; (1 << j) <= n; j++) {
for (int i = 0; i + (1 << j) - 1 < n; i++) {
rmq[i][j] = Math.min(rmq[i][j - 1], rmq[i + (1 << (j - 1))][j - 1]);
}
}
}
public static int query(int l, int r) {
int k = log2[r - l + 1];
return Math.min(rmq[l][k], rmq[r - (1 << k) + 1][k]);
}
}

@ -3791,9 +3791,69 @@ k < n
第071节 2023年5月第5周流行算法题目解析
来自字节
给定一个n*m的二维矩阵每个位置都是字符
U、D、L、R表示传送带的位置会被传送到 : 上、下、左、右
. 、O分别表示空地、目标一定只有一个目标点
可以在空地上选择上、下、左、右四个方向的一个
到达传送带的点会被强制移动到其指向的下一个位置
如果越界直接结束返回有几个点可以到达O点
来自学员问题
沿街有一排连续的房屋。每间房屋内都藏有一定的现金
现在有一位小偷计划从这些房屋中窃取现金
由于相邻的房屋装有相互连通的防盗系统,所以小偷 不会窃取相邻的房屋
小偷的 窃取能力 定义为他在窃取过程中能从单间房屋中窃取的 最大金额
给你一个整数数组 nums 表示每间房屋存放的现金金额
形式上,从左起第 i 间房屋中放有 nums[i] 美元
另给你一个整数 k ,表示窃贼将会窃取的最少房屋数
小偷一定要要窃取至少 k 间房屋,返回小偷的 最小 窃取能力
测试链接 : https://leetcode.cn/problems/house-robber-iv/
来自华为OD
如果n = 1打印
1***
如果n = 2打印
1***
3*** 2***
如果n = 3打印
1***
3*** 2***
4*** 5*** 6***
如果n = 4打印
1***
3*** 2***
4*** 5*** 6***
10** 9*** 8*** 7***
输入一个数n表示有多少行从1开始输出
奇数行输出奇数个数,奇数行正序,偶数行输出偶数个数,偶数行逆序
每个数后面加*补满四位中间空4个第n行顶格输出
字符串哈希原理和实现
字符串哈希+二分的例题
给定长为 n 的源串 s以及长度为 m 的模式串 p
要求查找源串中有多少子串与模式串匹配
s' 与 s 匹配,当且仅当 s' 与 s 长度相同,且最多有 k 个位置字符不同
其中 1 <= n, m <= 10^60 <= k <= 5
正方形矩阵哈希实现
二维哈希只适用于正方形的情况
如果想支持普通矩阵,需要更复杂度的过程,这里不做展开
如果一个正方形矩阵上下对称并且左右对称,对称的意思是互为镜像
那么称这个正方形矩阵叫做神奇矩阵
比如 :
1 5 5 1
6 3 3 6
6 3 3 6
1 5 5 1
这个正方形矩阵就是神奇矩阵
给定一个大矩阵n*m返回其中神奇矩阵的数目
1 <= n,m <= 1000
测试链接 : https://www.luogu.com.cn/problem/P2601

Loading…
Cancel
Save