(笔试题)php练习笔试题(一)
简答题与编程题完整优化解答
一、简答题(补充完善 + 修正细节)
1. 常用 Linux 命令及功能(已完善,补充说明)
命令 核心功能 补充说明
cat 显示文件内容 支持多文件拼接显示(cat file1 file2)、创建新文件(cat > newfile)
cd 改变目录路径 cd ~ 回到家目录、cd .. 回到上级目录、cd / 回到根目录
cp 复制文件 / 目录 复制目录需加 -r 参数(cp -r dir1 dir2),cp -p 保留文件属性
find 查找文件 / 目录 按名称查找:find / -name “test.txt”,按类型查找:find / -type f(文件)
grep 搜索、过滤信息 忽略大小写:grep -i “php” file,显示行号:grep -n “php” file
ls 列出目录信息 ls -l 详细列表(权限 / 大小 / 时间)、ls -a 显示隐藏文件(以.开头)
more 分页显示文件内容 向下翻页按空格,退出按q,仅支持向下滚动
rm 删除文件或目录 删除目录需加 -r(递归),强制删除加 -f(rm -rf dir,谨慎使用)
vi 调用 vi 文本编辑器 分为编辑模式(按i进入)、命令模式(按Esc退出编辑模式),:wq 保存退出
who 显示登录用户信息 查看当前系统登录的用户名称、登录终端、登录时间
2. HTTP/1.0 状态码含义(修正错误:503 非题目要求,补充细节)
状态码 含义 补充说明
200 成功 服务器正常处理请求,并返回请求的网页 / 资源
301 永久移动 请求的资源已永久迁移到新 URL,浏览器会自动缓存新地址
304 未修改 资源自上次请求后未发生变化,服务器不返回资源内容,减少带宽消耗
403 禁止访问 服务器识别了客户端身份,但拒绝提供请求资源(权限不足等)
404 未找到 服务器无法找到请求的网页 / 资源(URL 错误或资源已删除)
500 服务器内部错误 服务器在处理请求时发生未知异常(代码错误、配置错误等)
3. HTML 标签含义
标签 核心含义 补充说明
input 表单输入控件 常用type属性:text(文本框)、button(按钮)、radio(单选框)、checkbox(复选框)、file(文件上传)
form 表单容器 用于包裹表单控件,通过action指定表单提交地址,method指定提交方式(GET/POST)
script 脚本容器 用于嵌入 JavaScript 代码或引入外部 JS 文件(src属性指定外部文件路径)
style 样式容器 用于嵌入 CSS 样式代码,定义页面元素的外观样式(可通过type=”text/css”指定类型)
table 表格容器 用于创建 HTML 表格,需配合tr(表格行)、td(表格单元格)、th(表头单元格)使用
b 粗体文本标签 用于将包裹的文本设置为粗体(纯样式标签,推荐使用 CSSfont-weight: bold替代)
img 图片嵌入标签 用于在页面中插入图片,必须指定src(图片路径)和alt(图片替代文本,异常时显示)
4. public、protected、private 访问控制模式区别(补充完善细节)
这三种是面向对象语言中类成员(属性 / 方法)的访问控制修饰符,核心区别如下:
修饰符 类内部访问 类外部访问 子类继承访问 跨类访问(同命名空间)
public 允许 允许 允许(子类可直接访问 / 重写) 允许
protected 允许 禁止 允许(子类可直接访问 / 重写) 禁止
private 允许 禁止 禁止(子类无法继承,仅父类自身可访问) 禁止
补充:private修饰的成员仅对当前类可见,即使子类继承了该父类,也无法访问父类的private成员;protected修饰的成员是 “父子可见,外部不可见”;public修饰的成员是 “全局可见”。
5. PHP Session 运行机制及大型网站注意事项(补充完善)
(1)运行机制
当 PHP 脚本首次调用session_start()时,服务器会生成一个唯一的session_id(32 位字符串),同时创建一个对应的 session 文件(默认存储在服务器本地目录,由session.save_path指定);
服务器将session_id通过 HTTP 响应头(Set-Cookie)发送给客户端,客户端浏览器会将session_id存储在本地 Cookie 中(默认 Cookie 名:PHPSESSID);
当客户端再次发起请求时,会自动将 Cookie 中的session_id通过 HTTP 请求头发送给服务器;
服务器通过session_id找到对应的 session 文件,将文件中的序列化数据反序列化后,存入$_SESSION超全局数组供脚本使用;
脚本执行结束后,PHP 会自动将$_SESSION中的数据序列化,覆盖写入对应的 session 文件中,完成 session 数据持久化。
(2)大型网站注意事项
存储性能问题:默认文件存储方式在高并发下存在 I/O 瓶颈(单目录文件过多,定位耗时),解决方案:
修改session.save_path为多级目录(如/tmp/sess/[0-f]/[0-f]),需提前创建目录,减少单目录文件数量;
放弃文件存储,改用分布式缓存(Memcached/Redis)或数据库(MySQL)存储 session,提升读写效率;
session 同步问题:负载均衡架构下,客户端请求可能分发到不同服务器,导致 session 数据不一致,解决方案:
集中式存储:所有服务器共享同一个 session 存储(Redis/Memcached 集群 / 共享数据库);
会话粘滞:通过 LVS/Nginx 负载均衡配置,将同一客户端的所有请求绑定到同一台服务器(ip_hash 策略);
session 安全问题:session_id泄露可能导致会话劫持,解决方案:
设置 session Cookie 的httponly属性(防止 JS 窃取)、secure属性(仅 HTTPS 传输);
定期刷新session_id(session_regenerate_id(true)),尤其是用户登录 / 权限变更时;
session 过期问题:合理设置 session 过期时间(session.gc_maxlifetime),避免无效 session 占用存储资源。
6. MySQL 索引 / 主键 / 唯一索引 / 联合索引区别及性能影响(补充完善)
(1)核心区别
索引类型 唯一性 允许空值 数量限制 核心用途
普通索引(INDEX) 不要求唯一 允许 无限制 提升单字段 / 多字段的查询效率,无约束作用
主键(PRIMARY KEY) 强制唯一 不允许(非空约束) 一个表仅 1 个 唯一标识表中记录,兼具索引查询功能和数据完整性约束
唯一索引(UNIQUE) 强制唯一 允许(仅允许一个 NULL 值) 无限制 保证字段值唯一,提升查询效率,具备数据唯一性约束
联合索引(复合索引) 可选(可设置唯一联合索引) 允许(非主键场景) 无限制(字段数量建议≤5 个) 提升多字段组合查询的效率,遵循 “最左前缀匹配原则”
补充:主键是特殊的唯一索引,自带非空约束;联合索引是对多个字段组合创建的索引,查询时需满足最左前缀原则才能命中索引(如联合索引(a,b,c),支持a、a,b、a,b,c查询,不支持b、b,c、a,c查询)。
(2)对数据库性能的影响
读操作(查询):
正向影响:所有索引都能大幅提升读操作效率,通过索引排序后的数据集,可快速定位目标记录,避免全表扫描(尤其是大数据量表);
影响优先级:主键 > 唯一索引 > 联合索引 > 普通索引(主键默认是聚簇索引,查询效率最高);
写操作(插入 / 更新 / 删除):
负向影响:索引会降低写操作效率,因为写操作不仅要修改数据本身,还要同步更新对应的索引结构(重新排序、维护索引树);
影响程度:索引数量越多、索引字段越长、联合索引字段越多,写操作性能损耗越大;
平衡原则:合理创建索引(按需创建,避免冗余索引),优先为查询频率高的字段创建索引,减少对写操作频繁字段的索引创建。
7. MySQL varchar 和 char 的区别及查询效率(补充完善)
(1)核心区别
类型 长度特性 存储空间 空值处理 尾部空格处理
char 定长字符串 占用固定长度空间(如char(10),无论存储 1 个还是 10 个字符,都占用 10 个字符空间) 允许空值 自动删除尾部空格(存储时截断,查询时返回无尾部空格)
varchar 变长字符串 占用可变长度空间(实际存储长度 + 1/2 个字节的长度标识,如varchar(10)存储 3 个字符,占用 3+1=4 个字节) 允许空值 保留尾部空格(存储和查询均不修改)
补充:char的长度范围是 1~255,varchar的长度范围是 1~65535(受表字符集和行大小限制)。
(2)查询效率及原因
char查询效率更高,原因如下:
char是定长存储,数据在磁盘上是连续排列的,服务器无需额外解析长度标识,可直接定位数据位置,减少 IO 开销;
char存储时自动清理尾部空格,数据长度固定,索引结构更简单,查询时索引匹配效率更高;
varchar是变长存储,需要额外存储长度标识,查询时需先解析长度标识才能获取完整数据,且数据存储不连续,易产生内存碎片,影响查询效率;
例外场景:当存储的字符串长度差异较大且整体较短时,varchar的存储空间优势可能抵消其查询性能劣势,整体性能差异不明显。
8. MySQL 外连接、内连接与自连接的区别(补充完善)
连接类型 核心特性 结果集 语法示例
内连接(INNER JOIN) 只匹配满足连接条件的记录 仅返回左右表中同时满足连接条件的记录(无 NULL 补位) SELECT a.*, b.* FROM table_a a INNER JOIN table_b b ON a.id = b.a_id;
外连接 – 左连接(LEFT JOIN) 以左表为基准,匹配右表记录 返回左表所有记录,右表满足条件的记录正常显示,不满足的字段用 NULL 补位 SELECT a.*, b.* FROM table_a a LEFT JOIN table_b b ON a.id = b.a_id;
外连接 – 右连接(RIGHT JOIN) 以右表为基准,匹配左表记录 返回右表所有记录,左表满足条件的记录正常显示,不满足的字段用 NULL 补位 SELECT a.*, b.* FROM table_a a RIGHT JOIN table_b b ON a.id = b.a_id;
自连接(SELF JOIN) 表与自身进行连接(需起不同别名) 本质是内连接 / 外连接的特殊场景,将一张表视为两张独立表进行匹配 SELECT a.name, b.name FROM emp a INNER JOIN emp b ON a.manager_id = b.id;(查询员工及其经理)
补充:自连接无需特殊关键字,核心是为同一张表设置不同别名,用于查询表内层级关系(如部门上下级、员工经理)或关联自身数据。
9. 网络协议全称及中文解释(修正错误,补充完整)
协议缩写 英文全称 中文解释 核心用途
SMTP Simple Mail Transfer Protocol 简单邮件传输协议 用于发送电子邮件(从客户端 / 邮件服务器发送到目标邮件服务器)
POP3 Post Office Protocol Version 3 邮局协议版本 3 用于接收电子邮件(从邮件服务器下载到本地客户端,下载后服务器可删除原邮件)
HTTP HyperText Transfer Protocol 超文本传输协议 用于在客户端与 Web 服务器之间传输超文本资源(网页、图片等),明文传输,默认端口 80
FTP File Transfer Protocol 文件传输协议 用于在两台主机之间进行文件上传(PUT)和下载(GET),默认端口 21
DNS Domain Name System 域名系统(域名解析协议) 将人类易记的域名(如www.baidu.com)转换为机器可识别的 IP 地址(如 180.101.49.11)
10. JavaScript 基本数据类型(补充完善,区分 ES5/ES6)
JavaScript 的基本数据类型分为原始数据类型和引用数据类型,其中基本(原始)数据类型如下:
ES5 基本数据类型(5 种):
Number:数字类型(整数、浮点数、NaN、Infinity),如123、3.14、NaN;
String:字符串类型(单引号 / 双引号包裹的文本),如”PHP”、’JavaScript’;
Boolean:布尔类型(只有两个值),true(真)、false(假);
Null:空值类型(仅有一个值null),表示一个空的对象引用(手动设置);
Undefined:未定义类型(仅有一个值undefined),表示变量已声明但未赋值,或访问不存在的对象属性;
ES6 新增基本数据类型(1 种):
6. Symbol:唯一标识类型,用于创建唯一的属性名,避免属性名冲突,如const s = Symbol(‘test’);
11. 两列布局(右侧固定 200px,左侧自适应)(补充多种实现方案)
方案 1:浮动布局(原方案,优化兼容性)
html
预览
方案 2:Flex 布局(现代浏览器推荐,更简洁)
html
预览
方案 3:Grid 布局(灵活强大,适合复杂布局)
html
预览
二、项目设计(博客系统高并发设计,补充细节)
针对 1000 万用户、10 亿文章、日更新 10 万、日访问 5000 万、读写比 10:1 的博客 Tag 系统,设计方案如下:
1. 数据库设计(分库分表 + 索引优化 + 存储优化)
(1)分库分表策略
垂直分库:按业务模块拆分数据库(用户库、文章库、Tag 库、评论库),降低单库压力;
水平分表:
文章表:按create_time(创建时间)分表(如article_202401、article_202402),或按user_id哈希分表(article_00~article_99),解决 10 亿数据存储问题;
Tag 关联表:article_tag(文章与 Tag 的多对多关联)按article_id哈希分表,提升关联查询效率;
用户表:按user_id哈希分表(user_00~user_99),支撑 1000 万用户存储;
冷热数据分离:将近 1 年的活跃文章存储在高性能数据库(MySQL 集群),超过 1 年的冷文章迁移到低成本存储(如 ClickHouse、HDFS),降低热库压力。
(2)表结构优化(核心表)
Tag 表(tag):
sql
CREATE TABLE `tag` (
`tag_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT ‘Tag唯一ID’,
`tag_name` varchar(50) NOT NULL COMMENT ‘Tag名称(唯一)’,
`article_count` int(11) NOT NULL DEFAULT 0 COMMENT ‘关联文章数’,
`create_time` datetime NOT NULL COMMENT ‘创建时间’,
PRIMARY KEY (`tag_id`),
UNIQUE KEY `uk_tag_name` (`tag_name`) COMMENT ‘唯一索引,避免重复Tag’,
KEY `idx_article_count` (`article_count`) COMMENT ‘按关联文章数排序(热门Tag查询)’
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT ‘Tag基础表’;
文章 – Tag 关联表(article_tag_00,分表后缀):
sql
CREATE TABLE `article_tag_00` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT ‘主键ID’,
`article_id` bigint(20) NOT NULL COMMENT ‘文章ID’,
`tag_id` bigint(20) NOT NULL COMMENT ‘TagID’,
`create_time` datetime NOT NULL COMMENT ‘关联时间’,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_article_tag` (`article_id`,`tag_id`) COMMENT ‘避免文章重复关联同一Tag’,
KEY `idx_article_id` (`article_id`) COMMENT ‘按文章查Tag’,
KEY `idx_tag_id` (`tag_id`) COMMENT ‘按Tag查文章(核心查询)’
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT ‘文章-Tag关联表(分表)’;
文章表(article_202401,分表后缀):
sql
CREATE TABLE `article_202401` (
`article_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT ‘文章唯一ID’,
`user_id` bigint(20) NOT NULL COMMENT ‘作者ID’,
`title` varchar(200) NOT NULL COMMENT ‘文章标题’,
`content` text NOT NULL COMMENT ‘文章内容’,
`tag_ids` varchar(100) NOT NULL DEFAULT ” COMMENT ‘TagID逗号分隔(冗余存储,提升查询效率)’,
`create_time` datetime NOT NULL COMMENT ‘创建时间’,
`update_time` datetime NOT NULL COMMENT ‘更新时间’,
`status` tinyint(4) NOT NULL DEFAULT 1 COMMENT ‘状态(1-正常,0-删除)’,
PRIMARY KEY (`article_id`),
KEY `idx_user_id_create_time` (`user_id`,`create_time`) COMMENT ‘按作者查文章(排序)’,
KEY `idx_create_time` (`create_time`) COMMENT ‘按时间排序查询’
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT ‘文章表(按时间分表)’;
冗余存储:文章表中冗余tag_ids字段(如1,2,3),避免关联查询,提升文章详情页 Tag 展示效率;
索引优化:所有查询高频字段创建索引,联合索引遵循最左前缀原则,避免冗余索引。
(3)存储引擎选择
核心业务表(用户、文章、Tag):使用InnoDB(支持事务、行级锁、聚簇索引,提升并发读写效率);
日志表、统计报表表:使用MyISAM(查询速度快,不支持事务,适合读多写少的统计场景);
冷数据存储:使用ClickHouse(列式存储,适合大数据量查询分析,如历史文章统计)。
2. 系统框架设计(分层架构 + 缓存架构)
(1)分层架构(高内聚低耦合)
接入层:Nginx(负载均衡 + 静态资源缓存 + 反向代理),配置ip_hash或session_sticky解决 session 同步问题,同时开启 gzip 压缩,减少带宽消耗;
应用层:采用微服务架构拆分模块(用户服务、文章服务、Tag 服务、搜索服务),使用 PHP-FPM(或 Swoole)提供高性能接口,框架选用 Laravel/Symfony(成熟稳定,支持缓存、队列);
缓存层:分布式缓存集群(Redis 主从 + 哨兵 / 集群),缓存热点数据:
热点 Tag:缓存热门 Tag 列表(按article_count排序),过期时间 10 分钟;
热点文章:缓存访问量前 10 万的文章详情,过期时间 30 分钟,更新时主动删除缓存;
Tag – 文章关联:缓存热门 Tag 对应的文章列表,分页缓存(如tag_1_page_1),过期时间 10 分钟;
计数缓存:文章阅读量、Tag 关联数使用 Redisincr 计数,定时同步到数据库;
队列层:使用 Redis/RabbitMQ 异步处理非实时任务,降低同步压力:
文章发布:异步更新 Tag 关联数、异步生成文章静态化页面;
数据统计:异步统计文章阅读量、Tag 热度、用户行为数据;
消息推送:异步推送文章更新通知给关注用户;
数据层:MySQL 主从复制(读写分离),主库负责写操作(文章发布、Tag 创建、用户注册),从库负责读操作(文章查询、Tag 查询、用户信息查询),主从复制采用半同步复制,避免数据丢失。
(2)缓存架构(多级缓存,减少数据库压力)
一级缓存:本地缓存(PHP-FPM 进程缓存,如apc/xcache),缓存高频不变数据(如系统配置、Tag 字典表),减少分布式缓存访问;
二级缓存:Redis 分布式缓存(核心缓存),缓存热点数据,如上所述;
三级缓存:Nginx 静态缓存(缓存文章静态化页面、Tag 列表页面),配置过期时间,更新时主动清理缓存;
缓存策略:
缓存穿透:使用布隆过滤器(Bloom Filter)过滤不存在的文章 ID/TagID,避免无效查询;
缓存击穿:热点数据设置永久缓存(或互斥锁),避免缓存过期时大量请求穿透到数据库;
缓存雪崩:缓存过期时间随机化(如 10±2 分钟),同时部署 Redis 集群,避免单点故障。
3. 网络架构设计(高可用 + 高并发)
负载均衡:
第一层:LVS(四层负载均衡,负责分发请求到 Nginx 集群,抗高并发);
第二层:Nginx(七层负载均衡,负责分发请求到应用服务器集群,支持 URL 路由);
高可用部署:
所有核心服务(Nginx、PHP-FPM、Redis、MySQL)均采用集群部署,避免单点故障;
MySQL 主从切换:使用 MHA/Keepalived 实现主库故障自动切换到从库;
Redis 集群:采用主从 + 哨兵模式,实现主节点故障自动切换;
静态化处理:
文章详情页、Tag 文章列表页、用户博客首页采用静态化生成(PHP 脚本异步生成 HTML 文件),存储在 Nginx 本地或 CDN;
使用 CDN(内容分发网络)加速静态资源访问(文章 HTML、图片、CSS、JS),将静态资源分发到全国节点,降低源站压力,提升用户访问速度;
搜索优化:
文章全文搜索:放弃 MySQL like 查询,使用 Elasticsearch(ES)搭建搜索集群,将文章标题、内容、Tag 同步到 ES,支持分词搜索、模糊搜索,提升搜索效率;
Tag 搜索:ES 中创建 Tag 索引,支持 Tag 名称模糊匹配,提升 Tag 查找效率;
监控与扩容:
监控系统:部署 Zabbix/Prometheus,监控服务器负载、数据库性能、缓存命中率、接口响应时间,设置告警阈值;
弹性扩容:基于云服务器(阿里云 / 腾讯云),支持按访问量自动扩容应用服务器、缓存服务器,应对流量峰值。
三、编程题(优化修正,保证可运行)
编程题 1:顺序查找与二分查找算法
(1)顺序查找(线性查找)
算法描述
顺序查找是一种简单的查找算法,无需数组有序,从数组的第一个元素开始,依次遍历数组元素,与目标值进行比较,找到则返回索引,遍历结束未找到则返回 – 1。
优化版实现(PHP,支持有序 / 无序数组,提前终止)
php
运行
/**
* 顺序查找(优化版:遍历到目标值立即返回,减少无效遍历)
* @param array $arr 待查找数组
* @param mixed $target 目标值
* @return int 找到返回索引,未找到返回-1
*/
function sequentialSearch($arr, $target) {
$len = count($arr);
// 遍历数组,依次比较
for ($i = 0; $i < $len; $i++) { if ($arr[$i] === $target) { return $i; // 找到目标值,立即返回索引 } } return -1; // 未找到目标值 } // 测试 $arr = [3, 1, 4, 1, 5, 9, 2, 6]; $target = 5; $index = sequentialSearch($arr, $target); echo “顺序查找:目标值{$target}的索引是:{$index}”; // 输出:4 效率分析 时间复杂度:最好 O (1)(目标值在数组首位),最坏 O (n)(目标值在数组末尾或不存在),平均 O (n); 空间复杂度:O (1)(无需额外空间); 适用场景:小规模数组、无序数组。 (2)二分查找(折半查找) 算法描述 二分查找要求数组必须有序,通过不断缩小查找范围实现查找: 定义左指针left(初始 0)和右指针right(初始数组长度 – 1); 计算中间索引mid = floor(($left + $right) / 2); 比较$arr[$mid]与目标值: 相等:返回mid(找到目标值); $arr[$mid] > $target:目标值在左半部分,更新right = $mid – 1;
$arr[$mid] < $target:目标值在右半部分,更新left = $mid + 1; 重复步骤 2-3,直到left > right(未找到目标值,返回 – 1)。
PHP 实现
php
运行
/**
* 二分查找(仅支持有序数组)
* @param array $sortedArr 有序数组(升序)
* @param mixed $target 目标值
* @return int 找到返回索引,未找到返回-1
*/
function binarySearch($sortedArr, $target) {
$left = 0;
$right = count($sortedArr) – 1;
while ($left <= $right) { $mid = floor(($left + $right) / 2); // 计算中间索引 if ($sortedArr[$mid] === $target) { return $mid; // 找到目标值 } elseif ($sortedArr[$mid] > $target) {
$right = $mid – 1; // 目标值在左半部分
} else {
$left = $mid + 1; // 目标值在右半部分
}
}
return -1; // 未找到目标值
}
// 测试
$sortedArr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
$target = 6;
$index = binarySearch($sortedArr, $target);
echo “二分查找:目标值{$target}的索引是:{$index}”; // 输出:5
效率分析
时间复杂度:最好 O (1),最坏 O (log₂n),平均 O (log₂n)(效率远高于顺序查找);
空间复杂度:O (1)(迭代实现),O (log₂n)(递归实现,栈空间开销);
适用场景:大规模有序数组。
编程题 2:带标签忽略的字符串截取(修正原代码 bug,优化逻辑)
需求回顾
和不计算在截取长度内;
保留原有标签,若截取后标签未闭合,删除未闭合的开始标签;
示例:123abc456def789,截取 5 返回123ab,截取 8 返回123abc45。
优化实现(PHP)
php
运行
/**
* 带标签忽略的字符串截取
* @param string $str 原始字符串
* @param int $num 要截取的有效长度(排除标签)
* @return string 截取后的字符串
*/
function newsubstr($str, $num) {
if ($num <= 0) {
return ”;
}
$len = strlen($str);
$validLen = 0; // 有效字符长度(排除标签)
$result = ”; // 结果字符串
$tagStack = []; // 标签栈,用于处理未闭合标签
$i = 0;
while ($i < $len && $validLen < $num) {
// 匹配开始标签
if (substr($str, $i, 4) === ‘‘) {
$tagStack[] = ’em’; // 入栈
$result .= ‘‘;
$i += 4; // 跳过标签长度
continue;
}
// 匹配结束标签
if (substr($str, $i, 5) === ‘‘) {
if (!empty($tagStack) && end($tagStack) === ’em’) {
array_pop($tagStack); // 出栈
$result .= ‘‘;
$i += 5; // 跳过标签长度
} else {
// 无效标签,直接跳过
$i += 5;
}
continue;
}
// 普通字符,计入有效长度
$result .= $str[$i];
$validLen++;
$i++;
}
// 处理未闭合的标签(删除未闭合的开始标签)
if (!empty($tagStack)) {
$tag = end($tagStack);
$tagStr = “<{$tag}>”;
// 从结果字符串末尾反向查找未闭合的开始标签并删除
$tagPos = strrpos($result, $tagStr);
if ($tagPos !== false) {
$result = substr($result, 0, $tagPos);
}
}
return $result;
}
// 测试
$str = ‘123abc456def789′;
echo newsubstr($str, 5); // 输出:123ab
echo ‘
‘;
echo newsubstr($str, 8); // 输出:123abc45
编程题 3:猴子选大王(约瑟夫环问题,优化注释,验证可运行)
算法思路
约瑟夫环问题的递推公式:f(n,m) = (f(n-1,m) + m) % n,其中:
n:猴子总数;
m:步长;
f(n,m):n只猴子时大王的索引(从 0 开始);
初始条件:f(1,m) = 0(只有 1 只猴子时,大王索引为 0)。
优化实现(PHP)
php
运行
/**
* 猴子选大王(约瑟夫环问题)
* @param int $n 猴子总数
* @param int $m 数到第m只猴子踢出
* @return int 大王的编号(从1开始,若需从0开始直接返回$s)
*/
function monkeyKing($n, $m) {
if ($n < 1 || $m < 1) {
return -1; // 非法参数返回-1
}
$s = 0; // 大王的索引(从0开始)
// 递推计算n只猴子时的大王索引
for ($i = 2; $i <= $n; $i++) {
$s = ($s + $m) % $i;
}
return $s + 1; // 转换为从1开始的编号
}
// 测试
$n = 6; // 6只猴子
$m = 2; // 数到2踢出
$kingNo = monkeyKing($n, $m);
echo “猴子总数:{$n},步长:{$m},大王编号:{$kingNo}”; // 输出:5(索引4,编号5)
编程题 4:翻转字符串中的单词(不使用内置函数,优化逻辑)
需求回顾
输入”This is PHP”,输出”PHP is This”,不使用 PHP 内置的array_reverse等函数。
优化实现(PHP,纯手动实现翻转)
php
运行
/**
* 翻转字符串中的单词(不使用内置翻转函数)
* @param string $str 原始字符串
* @return string 翻转后的字符串
*/
function myrev($str) {
// 第一步:将字符串按空格分割为单词数组(手动分割,不使用explode)
$arr = [];
$word = ”;
$len = strlen($str);
for ($i = 0; $i < $len; $i++) {
if ($str[$i] !== ‘ ‘) {
$word .= $str[$i];
} else {
if (!empty($word)) {
$arr[] = $word;
$word = ”;
}
}
}
// 添加最后一个单词
if (!empty($word)) {
$arr[] = $word;
}
// 第二步:手动翻转数组(不使用array_reverse)
$num = count($arr);
for ($i = 0; $i < floor($num / 2); $i++) {
// 交换$i和$num-$i-1位置的元素
$temp = $arr[$i];
$arr[$i] = $arr[$num – $i – 1];
$arr[$num – $i – 1] = $temp;
}
// 第三步:手动拼接数组为字符串(不使用implode)
$result = ”;
for ($i = 0; $i < $num; $i++) {
$result .= $arr[$i];
// 不是最后一个单词,添加空格
if ($i !== $num – 1) {
$result .= ‘ ‘;
}
}
return $result;
}
// 测试
$str = ‘This is PHP’;
$reversedStr = myrev($str);
echo “原始字符串:{$str}
“;
echo “翻转后字符串:{$reversedStr}”; // 输出:PHP is This