TCHAR,大小写敏感/不敏感的KMP算法

本文深入讲解了KMP算法的实现细节,包括如何构建next数组来提高字符串匹配效率,并提供了大小写敏感及不敏感两种情况下的代码实现。

// ********************************* //
// kmp by Ceja
// ********************************* //

#include <tchar.h>


// kmp ==============
inline int* GetNextVal_i(const TCHAR *s, int &len);
inline int* GetNextVal(const TCHAR *s, int &len)
{
 len = (int)_tcslen(s);
 int *next = new int[len];
 int i = 0;
 int j = -1;
 next[0] = -1;
 while(i<len-1)//注意这里跟KMP函数里面的不同
 {
  if(j==-1 || s[i]==s[j])
  {
  ++i;
  ++j;
  next[i] = j;
  }
  else
  {
  j = next[j];
  }
 }
 return next;
}

// kmp 算法, 大小写敏感
inline int KMP(const TCHAR *s, const TCHAR *t)
{
 int slen,tlen;
 int i,j;
 int *next = GetNextVal(t, tlen);
 slen = (int)_tcslen(s);
 i = 0;
 j = 0;
 while(i<slen && j<tlen)
 {
  if(j==-1 || s[i]==t[j])
  {
  ++i;
  ++j;
  }
  else
  {
  j = next[j];
  }
 }

 delete[] next;

 if(j==tlen)
  return i - tlen;
 return -1;
}

// kmp 算法, 大小写不敏感
inline int KMP_i(const TCHAR *s, const TCHAR *t)
{
 int slen,tlen;
 int i,j;
 int *next = GetNextVal_i(t, tlen);
 slen = (int)_tcslen(s);
 i = 0;
 j = 0;
 while(i<slen && j<tlen)
 {
  if(j==-1 || ( ((_T('a') <= s[i] && _T('z') >= s[i])?(s[i] - 32):s[i]) == ((_T('a') <= t[j] && _T('z') >= t[j])?(t[j] - 32):t[j])))
  {
  ++i;
  ++j;
  }
  else
  {
  j = next[j];
  }
 }

 delete[] next;

 if(j==tlen)
  return i - tlen;
 return -1;
}

inline int* GetNextVal_i(const TCHAR *s, int &len)
{
 len = (int)_tcslen(s);
 int *next = new int[len];
 int i = 0;
 int j = -1;
 next[0] = -1;
 while(i<len-1)//注意这里跟KMP函数里面的不同
 {
  if(j==-1 || ( ((_T('a') <= s[i] && _T('z') >= s[i])?(s[i] - 32):s[i]) == ((_T('a') <= s[j] && _T('z') >= s[j])?(s[j] - 32):s[j])))
  {
  ++i;
  ++j;
  next[i] = j;
  }
  else
  {
  j = next[j];
  }
 }
 return next;
}



/// ***** end of kmp

<think> 我们正在使用VS2022和EasyX图形库来修改贪吃蛇游戏代码。目标包括: 1. 修复原有代码中的逻辑错误(例如游戏循环结构问题) 2. 优化游戏体验(例如添加分数、游戏结束处理等) 3. 确保代码符合C语言标准,并在EasyX环境下运行 原始代码存在以下主要问题: - 游戏开始和结束的逻辑混乱,存在多个循环嵌套,导致控制流复杂 - 没有实现分数的记录和显示 - 游戏结束后的重新开始逻辑完善 我们将按照以下步骤修改: 1. 重构主循环,使游戏状态清晰(开始界面、游戏进行、结束界面) 2. 添加分数系统,每吃一个食物分数增加 3. 完善游戏结束的条件(撞墙或撞自身)并显示分数 4. 实现重新开始功能 修改后的代码结构如下: - 定义全局游戏状态(开始、进行中、结束) - 在主循环中根据状态绘制同界面 - 分数作为全局变量 同时注意: - 使用键盘控制方向时防止180度转向 - 食物的生成避免与蛇身重叠 - 蛇的移动速度控制 我们将按照以下步骤编写代码: 步骤1:包含头文件和定义常量、结构体 步骤2:声明全局变量(游戏状态、分数、蛇和食物的结构等) 步骤3:初始化函数(初始化图形窗口、蛇的初始位置、食物等) 步骤4:绘制函数(绘制网格、蛇、食物、开始界面、结束界面) 步骤5:移动和方向控制函数 步骤6:食物检测和生成函数 步骤7:碰撞检测函数 步骤8:主循环,根据游戏状态切换 由于原始代码较长,我们逐步修改。以下为修改后的完整代码: </think> 下面是根据您的需求,使用 VS2022 和 EasyX 图形库修改完善的贪吃蛇游戏代码: ```c #include <graphics.h> // EasyX 图形库 #include <conio.h> // 控制台输入输出 #include <time.h> // 时间函数 #include <stdio.h> // 标准输入输出 #define NODE_WIDTH 30 // 网格宽度 #define MAX_LENGTH 100 // 蛇的最大长度 #define SPEED 150 // 初始游戏速度(毫秒) // 游戏状态枚举 enum GameState { START_SCREEN, PLAYING, GAME_OVER }; // 方向枚举 enum Direction { UP, DOWN, LEFT, RIGHT }; // 节点结构体 typedef struct { int x; int y; } Node; // 游戏全局变量 GameState gameState = START_SCREEN; int score = 0; int speed = SPEED; Node snake[MAX_LENGTH]; int snakeLength = 5; Direction currentDir = RIGHT; Node food; bool foodExist = false; // 初始化游戏 void initGame() { // 初始化蛇的位置 for (int i = 0; i < snakeLength; i++) { snake[i].x = 10 - i; snake[i].y = 10; } // 初始化分数和速度 score = 0; speed = SPEED; currentDir = RIGHT; // 生成第一个食物 foodExist = false; } // 绘制网格 void drawGrid() { setlinecolor(RGB(200, 200, 200)); // 浅灰色网格线 // 绘制水平线 for (int y = 0; y < 600; y += NODE_WIDTH) { line(0, y, 800, y); } // 绘制垂直线 for (int x = 0; x < 800; x += NODE_WIDTH) { line(x, 0, x, 600); } } // 绘制蛇 void drawSnake() { for (int i = 0; i < snakeLength; i++) { int left = snake[i].x * NODE_WIDTH; int top = snake[i].y * NODE_WIDTH; int right = (snake[i].x + 1) * NODE_WIDTH; int bottom = (snake[i].y + 1) * NODE_WIDTH; if (i == 0) { // 蛇头 - 红色 setfillcolor(RGB(255, 50, 50)); solidrectangle(left, top, right, bottom); // 眼睛 - 黑色 setfillcolor(BLACK); if (currentDir == RIGHT) { solidcircle(right - 5, top + 8, 3); solidcircle(right - 5, bottom - 8, 3); } else if (currentDir == LEFT) { solidcircle(left + 5, top + 8, 3); solidcircle(left + 5, bottom - 8, 3); } else if (currentDir == UP) { solidcircle(left + 8, top + 5, 3); solidcircle(right - 8, top + 5, 3); } else { solidcircle(left + 8, bottom - 5, 3); solidcircle(right - 8, bottom - 5, 3); } } else { // 蛇身 - 绿色 setfillcolor(RGB(50, 200, 50)); solidrectangle(left, top, right, bottom); // 蛇身边框 - 深绿色 setlinecolor(RGB(20, 160, 20)); rectangle(left, top, right, bottom); } } } // 生成食物 void generateFood() { if (!foodExist) { // 确保食物会出现在蛇身上 bool validPosition; do { validPosition = true; food.x = rand() % (800 / NODE_WIDTH); food.y = rand() % (600 / NODE_WIDTH); for (int i = 0; i < snakeLength; i++) { if (snake[i].x == food.x && snake[i].y == food.y) { validPosition = false; break; } } } while (!validPosition); foodExist = true; } } // 绘制食物 void drawFood() { if (foodExist) { int left = food.x * NODE_WIDTH; int top = food.y * NODE_WIDTH; int right = (food.x + 1) * NODE_WIDTH; int bottom = (food.y + 1) * NODE_WIDTH; // 食物 - 黄色圆形 setfillcolor(YELLOW); solidcircle((left + right) / 2, (top + bottom) / 2, NODE_WIDTH / 2 - 2); // 高光效果 setfillcolor(RGB(255, 255, 200)); solidcircle((left + right) / 2 - 5, (top + bottom) / 2 - 5, 3); } } // 移动蛇 void moveSnake() { // 移动蛇身(从尾部向前移动) for (int i = snakeLength - 1; i > 0; i--) { snake[i] = snake[i - 1]; } // 根据方向移动蛇头 switch (currentDir) { case UP: snake[0].y--; break; case DOWN: snake[0].y++; break; case LEFT: snake[0].x--; break; case RIGHT: snake[0].x++; break; } } // 检测碰撞 bool checkCollision() { // 检测边界碰撞 if (snake[0].x < 0 || snake[0].x >= 800 / NODE_WIDTH || snake[0].y < 0 || snake[0].y >= 600 / NODE_WIDTH) { return true; } // 检测自身碰撞(从第2节开始检查) for (int i = 1; i < snakeLength; i++) { if (snake[0].x == snake[i].x && snake[0].y == snake[i].y) { return true; } } return false; } // 检测食物 void checkFood() { if (foodExist && snake[0].x == food.x && snake[0].y == food.y) { // 增加蛇的长度(超过最大值) if (snakeLength < MAX_LENGTH) { snake[snakeLength] = snake[snakeLength - 1]; snakeLength++; } // 增加分数 score += 10; // 游戏加速 if (speed > 50) { speed -= 2; } // 生成新食物 foodExist = false; generateFood(); } } // 绘制开始界面 void drawStartScreen() { settextstyle(40, 0, _T("宋体")); settextcolor(RGB(50, 150, 200)); outtextxy(280, 150, _T("贪吃蛇游戏")); settextstyle(20, 0, _T("宋体")); settextcolor(WHITE); outtextxy(300, 250, _T("按空格键开始游戏")); settextstyle(15, 0, _T("宋体")); outtextxy(280, 300, _T("方向键: 控制蛇的移动")); outtextxy(280, 330, _T("P键: 暂停游戏")); outtextxy(280, 360, _T("R键: 重新开始游戏")); } // 绘制游戏结束界面 void drawGameOverScreen() { settextstyle(40, 0, _T("宋体")); settextcolor(RED); outtextxy(300, 150, _T("游戏结束!")); settextstyle(25, 0, _T("宋体")); settextcolor(YELLOW); // 显示得分 TCHAR scoreStr[50]; _stprintf_s(scoreStr, _T("得分: %d"), score); outtextxy(350, 220, scoreStr); settextstyle(20, 0, _T("宋体")); settextcolor(WHITE); outtextxy(300, 280, _T("按空格键重新开始")); outtextxy(300, 320, _T("按ESC键退出游戏")); } // 绘制游戏界面 void drawGameScreen() { // 显示分数 settextstyle(20, 0, _T("宋体")); settextcolor(WHITE); TCHAR scoreStr[30]; _stprintf_s(scoreStr, _T("得分: %d"), score); outtextxy(20, 20, scoreStr); // 显示长度 TCHAR lengthStr[30]; _stprintf_s(lengthStr, _T("长度: %d"), snakeLength); outtextxy(150, 20, lengthStr); // 显示速度 TCHAR speedStr[30]; _stprintf_s(speedStr, _T("速度: %d"), SPEED - speed + 100); outtextxy(280, 20, speedStr); } // 处理键盘输入 void handleInput() { if (_kbhit()) { char key = _getch(); // 开始界面和结束界面的空格键处理 if ((gameState == START_SCREEN || gameState == GAME_OVER) && key == ' ') { initGame(); gameState = PLAYING; return; } // 游戏结束界面的ESC键处理 if (gameState == GAME_OVER && key == 27) { // 27 = ESC closegraph(); exit(0); } // 游戏中的输入处理 if (gameState == PLAYING) { switch (key) { case 'w': case 'W': case 72: // 上箭头 if (currentDir != DOWN) currentDir = UP; break; case 's': case 'S': case 80: // 下箭头 if (currentDir != UP) currentDir = DOWN; break; case 'a': case 'A': case 75: // 左箭头 if (currentDir != RIGHT) currentDir = LEFT; break; case 'd': case 'D': case 77: // 右箭头 if (currentDir != LEFT) currentDir = RIGHT; break; case 'p': case 'P': // 暂停游戏 { settextstyle(30, 0, _T("宋体")); settextcolor(YELLOW); outtextxy(330, 250, _T("游戏暂停")); settextstyle(20, 0, _T("宋体")); outtextxy(320, 300, _T("按任意键继续")); _getch(); } break; case 'r': case 'R': // 重新开始 initGame(); break; } } } } int main() { // 初始化图形窗口 initgraph(800, 600); // 设置随机种子 srand((unsigned)time(NULL)); // 初始化游戏 initGame(); // 主游戏循环 while (true) { // 处理输入 handleInput(); // 清屏 cleardevice(); // 设置背景色 setbkcolor(RGB(40, 40, 60)); // 根据游戏状态绘制同界面 switch (gameState) { case START_SCREEN: drawStartScreen(); break; case PLAYING: // 生成食物 generateFood(); // 移动蛇 moveSnake(); // 检测碰撞 if (checkCollision()) { gameState = GAME_OVER; } // 检测食物 checkFood(); // 绘制游戏元素 drawGrid(); drawSnake(); drawFood(); drawGameScreen(); // 控制游戏速度 Sleep(speed); break; case GAME_OVER: drawGrid(); drawSnake(); drawFood(); drawGameOverScreen(); break; } // 刷新显示 FlushBatchDraw(); } closegraph(); return 0; } ``` ### 主要修改和增强功能: 1. **游戏状态管理** - 添加了 `GameState` 枚举管理三种状态:开始界面、游戏进行中、游戏结束 - 状态切换逻辑清晰,用户体验更流畅 2. **分数系统** [^1] - 添加分数显示和计算逻辑 - 每吃一个食物增加10分 - 在游戏界面顶部显示当前分数、蛇长度和游戏速度 3. **碰撞检测优化** [^2] - 边界碰撞检测:当蛇头超出游戏区域时游戏结束 - 自身碰撞检测:蛇头碰到自身任何部位游戏结束 - 改进了碰撞检测算法,避免无效检测 4. **游戏速度系统** - 初始速度150ms,每吃一个食物速度增加(数值减少) - 顶部显示当前速度级别(数值越大速度越快) 5. **图形界面增强** - 蛇头添加眼睛,根据移动方向显示同位置 - 食物使用圆形绘制,添加高光效果 - 网格线使用浅灰色,更美观 - 添加详细的游戏指引说明 6. **操作功能增强** - 添加暂停功能(按P键) - 添加重新开始功能(按R键) - 游戏结束时可选择重新开始或退出 7. **代码结构优化** - 模块化功能函数,逻辑更清晰 - 使用全局游戏状态变量简化流程控制 - 添加详细的注释说明 ### 编译说明: 1. 在VS2022中创建C++空项目 2. 安装EasyX图形库(从官网下载安装) 3. 将代码保存为.cpp文件并添加到项目 4. 项目属性设置: - 配置属性→常规→字符集:使用多字节字符集 - 配置属性→C/C++→预处理器:添加`_CRT_SECURE_NO_WARNINGS` 5. 编译运行即可 ### 操作指南: - **开始游戏**:空格键 - **移动方向**:方向键或WASD - **暂停游戏**:P键 - **重新开始**:R键 - **退出游戏**:ESC键(游戏结束时)
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值