D. String Game

字符串匹配优化算法
本文介绍了一种通过二分查找优化字符串匹配的过程。该算法利用二分法确定可以在字符串t中删除的最大字符数量,以保持其包含字符串p作为子串。通过预先计算标记数组并检查每个可能的子串状态,确保了p始终是t的有效子串。

题意:给你 一个字符串 t, 一个字符串 p,在给你一个序列a[i], 去掉 字符串中 下标为 a[i] 的字符。问你求出序列个数的最大值,使得p为t 的子字符串。

解法: 有字符串 t,p;
如果在 ,m time后 p 刚好是 t 的子串。
那么在 m-1 次前肯定是可以去掉字符的,m-1次之后肯定是不能去掉字符的。
所以直接 2 分区间 ,得到 m. 如何 m 次移动是合法的话, 就增加m,反之减小m.

#include <bits\stdc++.h>
using namespace std;

int a[200005], mark[200005], n,m;
string t,p;

int check(int num)
{
    for(int i=0;i<n;i++) mark[i] = 0;
    for(int i=0;i<num;i++) mark[ a[i] ] = 1;
    int leng=0, start = 0;
    for(int i=0;i<n;i++)
    {
        if(!mark[i])
        {
            if(t[i] == p[start])
            {
                leng++;
                start++;
                if(leng >= m) // t 字符串中海油 字符串 p 
                {
                    return 1;
                }
            }
        }
    }
    return 0;
}

int main()
{
    cin>>t>>p;
    n = t.length(),m = p.length();
    for(int i=0;i<n;i++) 
    {
        cin>>a[i];
        a[i]-=1;
    }
    int l = 1, r = n, ans=0;
    while(l <= r)
    {
        int mid = (l+r)/2;
        if(check(mid)) 
        {
            ans = max(ans, mid);
            l = mid+1; 
        } 
        else r = mid-1;
    }
    printf("%d\n",ans);
    return 0;
} 
package game.game2048; /** * @BelongsProject: TimeGame * @BelongsPackage: game * @Author: 土里埋埋埋埋埋 * @CreateTime: 2025-07-31 11:04 * @Description: 数据库操作 * @Version: 1.0 */ import java.sql.*; import java.util.ArrayList; import java.util.List; public class GameDAO { private Connection connection; // 登录数据库 public GameDAO() throws SQLException { connection = DriverManager.getConnection( "jdbc:mysql://117.72.189.79:3306/2048", "root", "Fbb608425@" ); } // 用户注册 public boolean registerUser(String username, String password) throws SQLException { String sql = "INSERT INTO users (username, password) VALUES (?, ?)"; try (PreparedStatement stmt = connection.prepareStatement(sql)) { stmt.setString(1, username); stmt.setString(2, password); return stmt.executeUpdate() > 0; } } // 用户登录 public int authenticateUser(String username, String password) throws SQLException { String sql = "SELECT id FROM users WHERE username = ? AND password = ?"; try (PreparedStatement stmt = connection.prepareStatement(sql)) { stmt.setString(1, username); stmt.setString(2, password); ResultSet rs = stmt.executeQuery(); if (rs.next()) { return rs.getInt("id"); } } return -1; } // 保存分数 public void saveScore(int userId, int score) throws SQLException { String sql = "INSERT INTO scores (user_id, score, is_timed_mode) VALUES (?, ?, ?)"; try (PreparedStatement stmt = connection.prepareStatement(sql)) { stmt.setInt(1, userId); stmt.setInt(2, score); boolean isTimedMode = false; stmt.setBoolean(3, isTimedMode); stmt.executeUpdate(); } } // 个人最高分 public List<String> maxscore(int user_id) throws SQLException { List<String> ranking = new ArrayList<>(); // 普通模式最高分 String normalSql = "SELECT MAX(score) AS max_normal_score " + "FROM scores WHERE user_id = ? AND is_timed_mode = false"; try (PreparedStatement stmt = connection.prepareStatement(normalSql)) { stmt.setInt(1, user_id); ResultSet rs = stmt.executeQuery(); if (rs.next()) { int maxScore = rs.getInt("max_normal_score"); ranking.add("最高分: " + maxScore); } } // 限时模式最高分 String timedSql = "SELECT MAX(score) AS max_timed_score " + "FROM scores WHERE user_id = ? AND is_timed_mode = true"; try (PreparedStatement stmt = connection.prepareStatement(timedSql)) { stmt.setInt(1, user_id); ResultSet rs = stmt.executeQuery(); if (rs.next()) { int maxScore = rs.getInt("max_timed_score"); ranking.add(""); } } return ranking; } // 总排行榜 public List<String> getGlobalRanking(boolean b) throws SQLException { List<String> ranking = new ArrayList<>(); boolean isTimedMode = false; String modeName = isTimedMode ? "限时模式" : "普通模式"; String sql = "SELECT u.username, s.score " + "FROM scores s " + "JOIN users u ON s.user_id = u.id " + "WHERE s.is_timed_mode = ? " + "ORDER BY score DESC LIMIT 10"; try (PreparedStatement stmt = connection.prepareStatement(sql)) { stmt.setBoolean(1, isTimedMode); ResultSet rs = stmt.executeQuery(); int rank = 1; while (rs.next()) { ranking.add(rank++ + ". " + rs.getString("username") + " - " + rs.getInt("score") + "分"); } } return ranking; } public List<String> getAllRankings() throws SQLException { List<String> allRankings = new ArrayList<>(); // 普通模式排行榜 allRankings.addAll(getGlobalRanking(false)); // 添加分隔线 allRankings.add("----------------------"); // 限时模式排行榜 allRankings.addAll(getGlobalRanking(true)); return allRankings; } // 个人历史记录 public List<String> getUserHistory(int userId) throws SQLException { List<String> history = new ArrayList<>(); String sql = "SELECT score, game_time, is_timed_mode FROM scores " + "WHERE user_id = ? ORDER BY game_time DESC LIMIT 10"; try (PreparedStatement stmt = connection.prepareStatement(sql)) { stmt.setInt(1, userId); ResultSet rs = stmt.executeQuery(); int count = 1; while (rs.next()) { String mode = rs.getBoolean("is_timed_mode") ? "限时" : "普通"; history.add(count++ + ".得分:" + rs.getInt("score") + " - " + rs.getTimestamp("game_time")); } } return history; } // 存档操作 public void saveGame(int userId, String saveName, String boardState, int score, boolean isTimedMode, int timeLimit, int remainingTime) throws SQLException { String sql = "INSERT INTO saves (user_id, save_name, board_state, score, " + "is_timed_mode, time_limit, remaining_time) " + "VALUES (?, ?, ?, ?, ?, ?, ?)"; try (PreparedStatement stmt = connection.prepareStatement(sql)) { stmt.setInt(1, userId); stmt.setString(2, saveName); stmt.setString(3, boardState); stmt.setInt(4, score); stmt.setBoolean(5, isTimedMode); stmt.setInt(6, timeLimit); stmt.setInt(7, remainingTime); stmt.executeUpdate(); } } // 获取用户存档 public List<GameSave> getUserSaves(int userId) throws SQLException { List<GameSave> saves = new ArrayList<>(); String sql = "SELECT id, save_name, score, save_time, " + "is_timed_mode, time_limit, remaining_time " + "FROM saves WHERE user_id = ? ORDER BY save_time DESC"; try (PreparedStatement stmt = connection.prepareStatement(sql)) { stmt.setInt(1, userId); ResultSet rs = stmt.executeQuery(); while (rs.next()) { saves.add(new GameSave( rs.getInt("id"), rs.getString("save_name"), rs.getString("board_state"), rs.getInt("score"), rs.getBoolean("is_timed_mode"), rs.getInt("time_limit"), rs.getInt("remaining_time"), rs.getTimestamp("save_time").toString() )); } } return saves; } // 读档操作 public GameSave loadGame(int saveId) throws SQLException { String sql = "SELECT board_state, score, is_timed_mode, " + "time_limit, remaining_time " + "FROM saves WHERE id = ?"; try (PreparedStatement stmt = connection.prepareStatement(sql)) { stmt.setInt(1, saveId); ResultSet rs = stmt.executeQuery(); if (rs.next()) { return new GameSave( saveId, "", // 存档名称不需要在加载时使用 rs.getString("board_state"), rs.getInt("score"), rs.getBoolean("is_timed_mode"), rs.getInt("time_limit"), rs.getInt("remaining_time"), null // 时间戳不需要 ); } } return null; } public void saveGame(int currentUserId, String saveName, String boardState, int score) { } // 增强的存档类 public static class GameSave { private int id; private String saveName; private String boardState; private int score; private boolean isTimedMode; private int timeLimit; private int remainingTime; private String timestamp; public GameSave(int id, String saveName, String boardState, int score, boolean isTimedMode, int timeLimit, int remainingTime, String timestamp) { this.id = id; this.saveName = saveName; this.boardState = boardState; this.score = score; this.isTimedMode = isTimedMode; this.timeLimit = timeLimit; this.remainingTime = remainingTime; this.timestamp = timestamp; } // Getter方法 public int getId() { return id; } public String getSaveName() { return saveName; } public String getBoardState() { return boardState; } public int getScore() { return score; } public boolean isTimedMode() { return isTimedMode; } public int getTimeLimit() { return timeLimit; } public int getRemainingTime() { return remainingTime; } public String getTimestamp() { return timestamp; } } } package game.game2048; /** * @BelongsProject: TimeGame * @BelongsPackage: Demo.game * @Author: 土里埋埋埋埋埋 * @CreateTime: 2025-07-31 11:51 * @Description: 游戏界面 * @Version: 1.0 */ import game.game2048.game.Direction; import game.game2048.game.Game; import java.sql.SQLException; import java.util.List; import java.util.Random; import java.util.Scanner; public class ConsoleUI { private static final int FIXED_TIME_LIMIT = 60; private Scanner scanner; private GameDAO gameDAO; private int currentUserId; public ConsoleUI() throws SQLException { scanner = new Scanner(System.in); gameDAO = new GameDAO(); currentUserId = -1; } public void start() throws SQLException { while (true) { if (currentUserId == -1) { showLoginMenu(); } else { showMainMenu(); } } } //获取登录菜单 private void showLoginMenu() { System.out.println("=== 2048 游戏 ==="); System.out.println("1. 登录"); System.out.println("2. 注册"); System.out.println("3. 退出"); System.out.print("请选择: "); int choice = scanner.nextInt(); scanner.nextLine(); try { switch (choice) { case 1: login(); break; case 2: register(); break; case 3: System.exit(0); default: System.out.println("无效选择!"); } } catch (SQLException e) { System.out.println("数据库错误: " + e.getMessage()); } } //登录界面 private void login() throws SQLException { System.out.print("用户名: "); String username = scanner.nextLine(); System.out.print("密码: "); String password = scanner.nextLine(); int userId = gameDAO.authenticateUser(username, password); if (userId != -1) { currentUserId = userId; captcha(); System.out.println("登录成功!"); } else { System.out.println("用户名或密码错误!"); } } //注册界面 private void register() throws SQLException { System.out.print("用户名: "); String username = scanner.nextLine(); System.out.print("密码: "); String password = scanner.nextLine(); if (gameDAO.registerUser(username, password)) { System.out.println("注册成功!"); } else { System.out.println("注册失败,用户名可能已存在!"); } } //登录验证 public void captcha() { while (true) { Scanner sc = new Scanner(System.in); String code = getCode(); System.out.println("验证码:" + code); System.out.print("请输入验证码:"); String userInput = sc.next(); if (validateCaptcha(code, userInput)) { System.out.println("验证码正确!"); break; } else { System.out.println("验证码错误!"); } } } public static String getCode() { String box = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; Random random = new Random(); StringBuilder captcha = new StringBuilder(); String codeBox = ""; for (int i = 0; i < 4; i++) { int index = random.nextInt(box.length()); char c = box.charAt(index); codeBox += c; } return codeBox; } public static boolean validateCaptcha(String captcha, String userInput) { return captcha.equalsIgnoreCase(userInput); } //游戏主菜单 private void showMainMenu() throws SQLException { System.out.println("=== 主菜单 ==="); System.out.println("1. 开始新游戏"); System.out.println("2. 加载存档"); System.out.println("3. 查看排行榜"); System.out.println("4. 查看历史记录"); System.out.println("5. 退出登录"); System.out.println("6. 退出游戏"); System.out.print("请选择: "); int choice = scanner.nextInt(); scanner.nextLine(); System.out.println(); switch (choice) { case 1: startGame(); break; case 2: loadGame(); break; case 3: showRankings(); break; case 4: showUserHistory(); break; case 5: currentUserId = -1; System.out.println("已退出登录"); break; case 6: System.exit(0); default: System.out.println("无效选择!"); } } //重新开始游戏 private void startGame() throws SQLException { System.out.println("=== 选择游戏模式 ==="); System.out.println("1. 普通模式"); System.out.println("2. 限时模式"); System.out.println("3. 返回主菜单"); System.out.print("请选择: "); int modeChoice = scanner.nextInt(); scanner.nextLine(); if (modeChoice == 3) return; // 返回主菜单 boolean isTimedMode = (modeChoice == 2); int timeLimit = isTimedMode ? FIXED_TIME_LIMIT : 0; while (true) { Game game = isTimedMode ? new Game(true, timeLimit) : // 限时模式 new Game(); // 普通模式 playGame(game, isTimedMode); System.out.println("请选择是否重新开始: 1 - 重新开始 2 - 返回菜单"); int res = scanner.nextInt(); scanner.nextLine(); if (res != 1) { break; } } } //游戏存档 private void loadGame() throws SQLException { List<GameDAO.GameSave> saves = gameDAO.getUserSaves(currentUserId); if (saves.isEmpty()) { System.out.println("没有找到存档!"); return; } System.out.println("=== 您的存档 ==="); System.out.printf("%-6s %-20s %-8s %-10s %-15s%n", "ID", "存档名称", "分数", "模式", "创建时间"); for (GameDAO.GameSave save : saves) { String modeInfo = save.isTimedMode() ? "限时模式" : "普通模式"; System.out.printf("%-6d %-20s %-8d %-10s %-15s%n", save.getId(), save.getSaveName(), save.getScore(), modeInfo, save.getTimestamp()); } System.out.print("输入存档ID: "); int saveId = scanner.nextInt(); scanner.nextLine(); GameDAO.GameSave gameSave = gameDAO.loadGame(saveId); if (gameSave != null) { // 创建游戏实例时传递模式信息 Game game = new Game(gameSave.getBoardState(), gameSave.getScore(), gameSave.isTimedMode(), gameSave.getTimeLimit(), gameSave.getRemainingTime()); // 传递模式信息给游戏循环 playGame(game, gameSave.isTimedMode()); } else { System.out.println("加载存档失败!"); } } //游戏规则 private void playGame(Game game, boolean isTimedMode) throws SQLException { List<String> userMaxScore = gameDAO.maxscore(currentUserId); while (!game.isGameOver()) { // 显示分数和时间 System.out.print("当前分数: " + game.getScore()); System.out.print("\t"); for (String rank : userMaxScore) { System.out.print(rank); } if (isTimedMode) { System.out.print("\t剩余时间: " + game.getRemainingTime() + "秒"); } System.out.println(); displayBoard(game.getBoard()); System.out.println("操作: w(上) s(下) a(左) d(右) q(退出) x(存档) z(撤回)"); System.out.print("输入操作: "); String input = scanner.nextLine().toLowerCase(); if (input.equals("q")) { return; } else if (input.equals("x")) { System.out.print("输入存档名称: "); String saveName = scanner.nextLine(); gameDAO.saveGame(currentUserId, saveName, game.getBoardState(), game.getScore()); System.out.println("游戏已保存!"); return; } else if (input.equals("z")) { if (game.canUndo()) { game.undo(); System.out.println("已撤回一步"); } else { System.out.println("无法撤回,一次仅可撤回一步"); } } else { Direction direction = null; switch (input) { case "w": direction = Direction.UP; break; case "s": direction = Direction.DOWN; break; case "a": direction = Direction.LEFT; break; case "d": direction = Direction.RIGHT; break; default: System.out.println("操作无效"); continue; } game.move(direction); } } // 游戏结束处理 displayBoard(game.getBoard()); if (isTimedMode && game.getRemainingTime() <= 0) { System.out.println("时间到! 最终分数: " + game.getScore()); } else { System.out.println("游戏结束! 最终分数: " + game.getScore()); } gameDAO.saveScore(currentUserId, game.getScore()); } //打印地图 private void displayBoard(int[][] board) { for (int i = 0; i < 4; i++) { System.out.print(""); for (int j = 0; j < 4; j++) { if (board[i][j] == 0) { System.out.print("\t0"); } else { System.out.print("\t"); System.out.print(board[i][j]); } } System.out.println(); } } //排行榜 private void showRankings() throws SQLException { System.out.println("=== 排行榜 ==="); List<String> globalRanking = gameDAO.getGlobalRanking(false); for (String rank : globalRanking) { System.out.println(rank); } } //游戏历史 private void showUserHistory() throws SQLException { System.out.println("=== 游戏历史 ==="); List<String> userHistory = gameDAO.getUserHistory(currentUserId); if (userHistory.isEmpty()) { System.out.println("暂无记录"); } else { for (String history : userHistory) { System.out.println(history); } } } } package game.game2048.util; import java.io.FileInputStream; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.util.Properties; /** * @BelongsProject: Game2048 * @BelongsPackage: game.game2048.util * @Author: 土里埋埋埋埋埋 * @CreateTime: 2025-08-07 16:21 * @Description: TODO * @Version: 1.0 */ public class DbProUtils { //驱动 private static String driver; //ID private static String url; //数据库用户名 private static String username; //数据库密码 private static String password; //静态代码块 static { try { Properties prop = new Properties(); prop.load(new FileInputStream("config/db.properties")); driver = prop.getProperty("driver"); url = prop.getProperty("url"); username = prop.getProperty("username"); password = prop.getProperty("password"); } catch (Exception e) { throw new RuntimeException(e); } } public static Connection getConnection(){ Connection conn = null; try { DbProUtils DbPropUtils = null; Class.forName(DbPropUtils.getDriver()); conn = DriverManager.getConnection(DbPropUtils.getUrl(), DbPropUtils.getUsername(), DbPropUtils.getPassword()); } catch (Exception e) { throw new RuntimeException(e); } return conn; } public static void closeConnection(Connection connection){ try { connection.close(); } catch (SQLException e) { throw new RuntimeException(e); } } public static String getDriver() { return driver; } public static String getUrl() { return url; } public static String getUsername() { return username; } public static String getPassword() { return password; } } package game.game2048.util; import java.io.FileInputStream; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.util.Properties; /** * @BelongsProject: Game2048 * @BelongsPackage: game.game2048.util * @Author: 土里埋埋埋埋埋 * @CreateTime: 2025-08-07 16:21 * @Description: TODO * @Version: 1.0 */ public class DbProUtils { //驱动 private static String driver; //ID private static String url; //数据库用户名 private static String username; //数据库密码 private static String password; //静态代码块 static { try { Properties prop = new Properties(); prop.load(new FileInputStream("config/db.properties")); driver = prop.getProperty("driver"); url = prop.getProperty("url"); username = prop.getProperty("username"); password = prop.getProperty("password"); } catch (Exception e) { throw new RuntimeException(e); } } public static Connection getConnection(){ Connection conn = null; try { DbProUtils DbPropUtils = null; Class.forName(DbPropUtils.getDriver()); conn = DriverManager.getConnection(DbPropUtils.getUrl(), DbPropUtils.getUsername(), DbPropUtils.getPassword()); } catch (Exception e) { throw new RuntimeException(e); } return conn; } public static void closeConnection(Connection connection){ try { connection.close(); } catch (SQLException e) { throw new RuntimeException(e); } } public static String getDriver() { return driver; } public static String getUrl() { return url; } public static String getUsername() { return username; } public static String getPassword() { return password; } } package game.game2048.game; /** * @BelongsProject: TimeGame * @BelongsPackage: Demo.game * @Author: 土里埋埋埋埋埋 * @CreateTime: 2025-07-31 11:14 * @Description: 方向 */ public enum Direction { UP, DOWN, LEFT, RIGHT } package game.game2048.game; /** * @BelongsProject: TimeGame * @BelongsPackage: Demo.game * @Author: 土里埋埋埋埋埋 * @CreateTime: 2025-07-31 11:14 * @Description: 游戏主体 */ import java.util.Random; import java.util.Timer; import java.util.TimerTask; public class Game { private static final int SIZE = 4; private static final double PROBABILITY_4 = 0.1; private int[][] board; //游戏地图 private int score; //游戏分数 private boolean gameOver; //游戏结束 private boolean moved; //游戏移动 private int[][] previousBoard; //上一步地图 private int previousScore; //上一步分数 private boolean canUndo; //是否重新开始 private int timeLimit; // 时间限制(秒) private int remainingTime; // 剩余时间(秒) private Timer timer; // 计时器 private boolean isTimedMode; // 是否开启计时器 //普通模式 public Game() { board = new int[SIZE][SIZE]; initializeBoard(); score = 0; gameOver = false; canUndo = false; this.isTimedMode = false; } // 限时模式 public Game(boolean isTimedMode, int timeLimit) { this(); this.isTimedMode = isTimedMode; this.timeLimit = timeLimit; this.remainingTime = timeLimit; if (isTimedMode) { startTimer(); } } public Game(String boardState, int score, boolean timedMode, int timeLimit, int remainingTime) { } // 启动计时器 private void startTimer() { timer = new Timer(); timer.scheduleAtFixedRate(new TimerTask() { @Override public void run() { synchronized (Game.this) { if (remainingTime > 0) { remainingTime--; } else { // 时间到,游戏结束 gameOver = true; timer.cancel(); } } } }, 1000, 1000); } // 停止计时器 public void stopTimer() { if (timer != null) { timer.cancel(); } } // 重新开始游戏 public void resetGame() { stopTimer(); score = 0; gameOver = false; canUndo = false; board = new int[SIZE][SIZE]; initializeBoard(); // 限时模式处理 if (isTimedMode) { remainingTime = timeLimit; startTimer(); } } //地图初始化 private void initializeBoard() { Random random = new Random(); // 四个角落的坐标 int[][] corners = {{0, 0}, {0, SIZE - 1}, {SIZE - 1, 0}, {SIZE - 1, SIZE - 1}}; // 随机选择一个角落放置第一个方格 int cornerIndex = random.nextInt(4); int row1 = corners[cornerIndex][0]; int col1 = corners[cornerIndex][1]; board[row1][col1] = random.nextDouble() < PROBABILITY_4 ? 4 : 2; // 找到所有非角落的空位 int[][] nonCornerPositions = new int[(SIZE * SIZE) - 4][2]; int index = 0; for (int i = 0; i < SIZE; i++) { for (int j = 0; j < SIZE; j++) { if ((i == 0 || i == SIZE - 1) && (j == 0 || j == SIZE - 1)) continue; nonCornerPositions[index][0] = i; nonCornerPositions[index][1] = j; index++; } } // 随机选择一个非角落的空位放置第二个方格 int nonCornerIndex = random.nextInt((SIZE * SIZE) - 4); int row2 = nonCornerPositions[nonCornerIndex][0]; int col2 = nonCornerPositions[nonCornerIndex][1]; board[row2][col2] = random.nextDouble() < PROBABILITY_4 ? 4 : 2; } private int[] compress(int[] row) { int[] newRow = new int[SIZE]; int index = 0; for (int value : row) { if (value != 0) { newRow[index++] = value; } } return newRow; } // 合并相邻相同的数字 private int[] merge(int[] row) { for (int i = 0; i < SIZE - 1; i++) { if (row[i] != 0 && row[i] == row[i + 1]) { row[i] *= 2; row[i + 1] = 0; } } return row; } // 加载存档的构造函数 public Game(String boardState, int score) { board = new int[4][4]; String[] values = boardState.split(","); int index = 0; for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { board[i][j] = Integer.parseInt(values[index++]); } } this.score = score; gameOver = false; } //控制移动 public void move(Direction direction) { if (gameOver || (isTimedMode && remainingTime <= 0)) { return; } savePreviousState(); moved = false; switch (direction) { case UP: moveUp(); break; case DOWN: moveDown(); break; case LEFT: moveLeft(); break; case RIGHT: moveRight(); break; } if (moved) { addRandomPieces(); gameOver = checkGameOver(); canUndo = true; } else { gameOver = checkGameOver(); canUndo = false; } savePreviousState(); moved = false; } // 矩阵转置 private void transpose() { for (int i = 0; i < 4; i++) { for (int j = i + 1; j < 4; j++) { int temp = board[i][j]; board[i][j] = board[j][i]; board[j][i] = temp; } } } // 上下翻转(行反转) private void reverseRows() { for (int j = 0; j < 4; j++) { // 对每一列 for (int i = 0; i < 2; i++) { // 只需要交换上半部分和下半部分 int temp = board[i][j]; board[i][j] = board[3 - i][j]; board[3 - i][j] = temp; } } } //上 private void moveUp() { for (int col = 0; col < 4; col++) { for (int row = 1; row < 4; row++) { if (board[row][col] != 0) { int currentRow = row; while (currentRow > 0 && board[currentRow - 1][col] == 0) { board[currentRow - 1][col] = board[currentRow][col]; board[currentRow][col] = 0; currentRow--; moved = true; } if (currentRow > 0 && board[currentRow - 1][col] == board[currentRow][col]) { board[currentRow - 1][col] *= 2; score += board[currentRow - 1][col]; board[currentRow][col] = 0; moved = true; } } } } } //下 private void moveDown() { reverseRows(); moveUp(); reverseRows(); } //左 private void moveLeft() { transpose(); moveUp(); transpose(); } //右 private void moveRight() { transpose(); reverseRows(); moveUp(); reverseRows(); transpose(); } //添加随机棋子 private void addRandomPieces() { int emptyCells = 0; for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { if (board[i][j] == 0) { emptyCells++; } } } if (emptyCells == 0) return; Random random = new Random(); int position = random.nextInt(emptyCells); int value = random.nextDouble() < 0.9 ? 2 : 4; int count = 0; for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { if (board[i][j] == 0) { if (count == position) { board[i][j] = value; return; } count++; } } } } //游戏结束条件 public boolean checkGameOver() { // 限时模式优先判断 if (isTimedMode && remainingTime <= 0) { return true; } // 常规棋盘判断 for (int i = 0; i < SIZE; i++) { for (int j = 0; j < SIZE; j++) { if (board[i][j] == 0) return false; if (j < SIZE - 1 && board[i][j] == board[i][j + 1]) { return false; } if (i < SIZE - 1 && board[i][j] == board[i + 1][j]) { return false; } } } return true; } // 获取剩余时间 public int getRemainingTime() { return remainingTime; } //获取棋盘 public String getBoardState() { StringBuilder sb = new StringBuilder(); for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { sb.append(board[i][j]); if (i != 3 || j != 3) { sb.append(","); } } } return sb.toString(); } //保存上一步操作 private void savePreviousState() { previousBoard = new int[SIZE][SIZE]; for (int i = 0; i < SIZE; i++) { System.arraycopy(board[i], 0, previousBoard[i], 0, SIZE); } previousScore = score; } //撤回操作 public void undo() { if (!canUndo) return; for (int i = 0; i < SIZE; i++) { System.arraycopy(previousBoard[i], 0, board[i], 0, SIZE); } score = previousScore; gameOver = false; canUndo = false; } //检查是否可以撤回 public boolean canUndo() { return canUndo; } public int[][] getBoard() { return board; } public int getScore() { return score; } public boolean isGameOver() { return gameOver; } } 将以上代码拆分为类
最新发布
11-18
XmlException: Attribute name and qualified name must be identical. file:///D:/Lua_Dating538/Assets/Plugins/Android/AndroidManifest.xml Line 3, position 61. Mono.Xml2.XmlTextReader.ReadStartTag () Mono.Xml2.XmlTextReader.ReadContent () Mono.Xml2.XmlTextReader.ReadContent () Mono.Xml2.XmlTextReader.Read () System.Xml.XmlTextReader.Read () System.Xml.XmlDocument.ReadNodeCore (System.Xml.XmlReader reader) System.Xml.XmlDocument.ReadNode (System.Xml.XmlReader reader) System.Xml.XmlDocument.Load (System.Xml.XmlReader xmlReader) UnityEditor.AndroidXmlDocument..ctor (System.String path) UnityEditor.AndroidManifest..ctor (System.String path) UnityEditor.Android.PostProcessAndroidPlayer.CopyMainManifest (System.String target, System.String playerPackage) UnityEditor.Android.PostProcessAndroidPlayer.PostProcessInternal (System.String stagingAreaData, System.String stagingArea, System.String playerPackage, System.String installPath, System.String companyName, System.String productName, BuildOptions options, UnityEditor.RuntimeClassRegistry usedClassRegistry) UnityEditor.Android.PostProcessAndroidPlayer.PostProcess (BuildTarget target, System.String stagingAreaData, System.String stagingArea, System.String playerPackage, System.String installPath, System.String companyName, System.String productName, BuildOptions options, UnityEditor.RuntimeClassRegistry usedClassRegistry) UnityEditor.Android.AndroidBuildPostprocessor.PostProcess (BuildPostProcessArgs args) UnityEditor.PostprocessBuildPlayer.Postprocess (BuildTarget target, System.String installPath, System.String companyName, System.String productName, Int32 width, Int32 height, System.String downloadWebplayerUrl, System.String manualDownloadWebplayerUrl, BuildOptions options, UnityEditor.RuntimeClassRegistry usedClassRegistry) (at C:/buildslave/unity/build/Editor/Mono/BuildPipeline/PostprocessBuildPlayer.cs:316) UnityEditor.HostView:OnGUI()
07-10
<think>我们正在解决Unity导出Android项目时出现的AndroidManifest.xml解析错误,具体错误信息是:XmlException Attribute name and qualified name must be identical。这个错误通常是由于AndroidManifest.xml文件中存在格式错误,特别是属性名称或命名空间使用不正确导致的。 根据引用[1]中提供的Unity导出的AndroidManifest.xml片段,我们可以看到其中包含UnityPlayerActivity的声明。在Unity导出Android项目时,生成的AndroidManifest.xml文件可能会因为某些原因(如重复属性、命名空间错误等)导致解析错误。 错误信息“Attribute name and qualified name must be identical”通常意味着在XML属性中,属性名和限定名(即带有命名空间前缀的属性)必须一致,也就是说,如果使用了命名空间前缀,那么整个属性名(包括前缀)必须完整且正确。 在Unity导出的AndroidManifest.xml中,常见的错误包括: 1. 重复的属性(例如,同一个属性被声明了两次)。 2. 使用了未声明的命名空间前缀。 3. 属性名书写错误(例如,大小写不一致,或者拼写错误)。 根据用户之前的问题,我们曾处理过`compileSdkVersion`错误,但这次错误不同,所以我们需要针对这个错误进行排查。 **解决方案步骤:** ### 步骤1:检查Unity导出的AndroidManifest.xml文件 打开Unity导出的Android项目,找到`AndroidManifest.xml`文件(通常在`src/main`目录下)。检查报错的行(根据错误信息,这里是第2行,第178个字符附近)。 ### 步骤2:查找重复属性或格式错误 在错误提示的位置(第2行,第178个字符)附近,检查是否有以下问题: - 重复的属性:例如,同一个属性名出现了两次(如`android:name`被写了两次)。 - 属性名拼写错误:例如,`android:name`写成了`android:namee`或者`android:Name`(注意XML是大小写敏感的)。 - 命名空间前缀使用错误:例如,使用了未定义的命名空间前缀(除了`android:`之外,可能还有其他自定义的前缀,但必须已声明)。 ### 步骤3:检查命名空间声明 在`<manifest>`标签的开始处,通常会有命名空间的声明,例如: ```xml <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.game"> ``` 确保命名空间`xmlns:android`已正确声明,并且所有使用`android:`前缀的属性都依赖于这个声明。 ### 步骤4:检查特殊字符 确保在属性值中,如果使用了特殊字符(如`&`、`<`等),必须进行转义(例如`&`转义为`&`,`<`转义为`<`)。在属性名中不允许出现特殊字符,所以属性名本身一般不会出现,但属性值可能会。 ### 步骤5:检查Unity版本和构建设置 如果经过上述检查未发现问题,考虑可能是Unity版本的问题。尝试以下操作: - 更新Unity到最新版本(或使用稳定版本)。 - 在Unity的Player Settings中,检查Android设置下的Publishing Settings(发布设置),确保勾选了“Custom Main Manifest”和“Custom Main Gradle Template”以使用自定义的清单文件(如果之前没有,可以尝试勾选并生成新的模板,然后基于模板修改)。 ### 步骤6:手动修改或替换AndroidManifest.xml 如果错误仍然存在,可以尝试以下方法: 1. 使用一个全新的AndroidManifest.xml文件替换当前文件(可以从Unity新建一个空项目导出,获取其AndroidManifest.xml,然后在此基础上修改)。 2. 在Unity中,通过菜单栏(Assets -> Open Android Manifest)打开默认的AndroidManifest.xml,检查是否有错误(注意:这个文件是Unity内部使用的模板,导出时会基于此生成)。 ### 步骤7:使用外部工具验证 将AndroidManifest.xml文件复制到Android Studio中,利用Android Studio的XML检查功能(打开文件,如果有错误会直接提示)来定位问题。 ### 示例:一个正确的Unity导出的AndroidManifest.xml开头部分 ```xml <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.yourcompany.yourgame" android:installLocation="preferExternal" android:versionCode="1" android:versionName="1.0"> <uses-sdk android:minSdkVersion="19" android:targetSdkVersion="29" /> <application android:label="@string/app_name" android:icon="@mipmap/app_icon" android:theme="@style/UnityThemeSelector"> <activity android:name="com.unity3d.player.UnityPlayerActivity" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> <meta-data android:name="unityplayer.UnityActivity" android:value="true" /> </activity> </application> </manifest> ``` **注意:** 在以上示例中,所有属性名都是正确的,没有重复,并且命名空间`android`已声明。 ### 步骤8:重新导出项目 修改后,重新从Unity导出Android项目,并再次尝试在Android Studio中构建。 ### 补充:引用[2]中提到的“Entry name AndroidManifest.xml collided”错误 虽然这不是当前的问题,但如果在构建过程中遇到这个错误,说明在打包时存在同名的文件冲突(通常是因为之前构建的APK或AAB文件还在同一目录下)。解决方案是删除之前构建的文件或更改输出路径。 ### 总结 本次错误的核心是XML格式错误,特别是属性名的书写问题。按照上述步骤逐一排查,应该能够解决。如果问题仍然存在,请提供具体的错误行内容,以便进一步分析。 §§相关问题§§ 1. Unity导出Android项目时如何避免AndroidManifest.xml错误? 2. 如何自定义Unity导出的AndroidManifest.xml文件? 3. 常见的Unity Android导出错误有哪些? 4. AndroidManifest.xml文件的语法规则是什么?</think>### 解决Unity导出Android项目时的AndroidManifest.xml解析错误 当Unity导出Android项目时出现`XmlException: Attribute name and qualified name must be identical`错误,通常是由于AndroidManifest.xml文件中的XML格式错误导致的。以下是完整的解决方案: #### 步骤1:定位问题根源(关键) 错误信息表明XML文件中存在**属性名格式错误**,常见原因包括: 1. 属性名使用了未声明的命名空间前缀(如`android:`前缀缺失) 2. 属性名中包含非法字符或空格 3. 属性值缺少引号或引号不匹配 4. 特殊字符(如`&`)未正确转义 #### 步骤2:检查并修复Unity生成的AndroidManifest.xml 1. **检查命名空间前缀** 确保所有Android属性都使用`android:`前缀: ```xml <!-- 错误示例 --> <activity name="com.unity3d.player.UnityPlayerActivity"> <!-- 正确修复 --> <activity android:name="com.unity3d.player.UnityPlayerActivity"> ``` 2. **检查特殊字符转义** 将`&`替换为`&`(常见于应用名称): ```xml <!-- 错误示例 --> <application android:label="Game & Adventure"> <!-- 正确修复 --> <application android:label="Game & Adventure"> ``` 3. **检查引号闭合** 确保所有属性值都有匹配的引号: ```xml <!-- 错误示例 --> <meta-data android:name=unityplayer.ForwardNativeEventsToDalvik android:value=true> <!-- 正确修复 --> <meta-data android:name="unityplayer.ForwardNativeEventsToDalvik" android:value="true"> ``` #### 步骤3:Unity特定配置修复 1. **更新Unity版本** 旧版本Unity(特别是2019.4之前的版本)存在AndroidManifest生成缺陷: - 升级到最新LTS版本(推荐2022.3+) 2. **重置Unity的Android配置**: ```csharp // 在Unity编辑器中操作: File > Build Settings > Android > Switch Platform Player Settings > Publishing Settings > Build > Reset Project Settings ``` 3. **自定义主清单(关键步骤)**: - 在`Player Settings > Publishing Settings`中勾选`Custom Main Manifest` - Unity会在`Assets/Plugins/Android`生成`AndroidManifest.xml` - 修改此文件而非自动生成的文件 #### 步骤4:解决文件冲突问题(引用[2]) 若遇到`Entry name AndroidManifest.xml collided`错误: 1. 删除项目中的临时文件: - 清除`<Project>/Library`目录 - 删除`<Project>/Temp`目录 2. 清理构建目录: - 删除之前生成的APK/AAB文件 - 更改新APK的输出路径 #### 步骤5:最终验证与构建 1. 使用XML验证工具检查: ```bash xmllint --noout AndroidManifest.xml ``` 2. 在Unity中重新导出: - 确保勾选`Export Project`而非直接构建APK - 在Android Studio中打开导出的项目进行最终构建 > **预防建议**: > 在Unity项目中维护自定义的AndroidManifest模板(位于`Assets/Plugins/Android`),避免每次导出自动生成[^1]。对于UnityPlayerActivity的配置,确保使用标准声明格式[^1]: > ```xml > <activity android:name="com.unity3d.player.UnityPlayerActivity" > android:label="@string/app_name"> > <intent-filter> > <action android:name="android.intent.action.MAIN" /> > <category android:name="android.intent.category.LAUNCHER" /> > </intent-filter> > <meta-data android:name="unityplayer.UnityActivity" > android:value="true" /> > </activity> > ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值