Files
ClassFeedback/output/CSP03-07_string使用2-教学演示.html

1133 lines
41 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CSP03-07 string 使用2 - 字符串进阶教学</title>
<!-- Fonts: Poppins for tech vibe -->
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;600;700&display=swap" rel="stylesheet">
<style>
/* ===========================================
CSS CUSTOM PROPERTIES (THEME)
Creative Voltage Style - Techy/Futuristic
=========================================== */
:root {
/* Colors - Deep space theme with neon accents */
--bg-primary: #0a0e27;
--bg-secondary: #111827;
--bg-card: rgba(17, 24, 39, 0.8);
--text-primary: #ffffff;
--text-secondary: #9ca3af;
--accent: #00ffcc;
--accent-glow: rgba(0, 255, 204, 0.3);
--accent-purple: #8b5cf6;
--accent-blue: #3b82f6;
/* Typography - Poppins for tech feel */
--font-display: 'Poppins', sans-serif;
--font-mono: 'Poppins Mono', monospace;
/* MUST use clamp() for responsive typography */
--title-size: clamp(2rem, 6vw, 4rem);
--subtitle-size: clamp(1.2rem, 3vw, 2rem);
--h1-size: clamp(1.8rem, 4vw, 3rem);
--h2-size: clamp(1.4rem, 3vw, 2rem);
--h3-size: clamp(1.1rem, 2vw, 1.5rem);
--body-size: clamp(0.9rem, 1.5vw, 1.1rem);
--code-size: clamp(0.8rem, 1.3vw, 1rem);
/* Spacing - MUST use clamp() */
--slide-padding: clamp(1.5rem, 4vw, 3rem);
--content-gap: clamp(1rem, 2vw, 2rem);
--element-gap: clamp(0.5rem, 1vw, 1rem);
/* Animation */
--ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
--duration-normal: 0.6s;
--duration-slow: 1s;
}
/* ===========================================
BASE STYLES
=========================================== */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
/* Lock html/body to viewport */
html, body {
height: 100%;
overflow-x: hidden;
}
html {
scroll-snap-type: y mandatory;
scroll-behavior: smooth;
}
/* Each slide = exact viewport height */
.slide {
width: 100vw;
height: 100vh;
height: 100dvh;
overflow: hidden;
scroll-snap-align: start;
display: flex;
flex-direction: column;
position: relative;
}
/* Content container with flex for centering */
.slide-content {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
max-height: 100%;
overflow: hidden;
padding: var(--slide-padding);
}
/* ALL typography uses clamp() */
body {
font-family: var(--font-display);
background: var(--bg-primary);
color: var(--text-primary);
}
h1, h2, h3 {
font-weight: 600;
margin-bottom: var(--element-gap);
line-height: 1.2;
}
h1 { font-size: var(--h1-size); }
h2 { font-size: var(--h2-size); }
h3 { font-size: var(--h3-size); }
p {
font-size: var(--body-size);
line-height: 1.6;
margin-bottom: var(--element-gap);
}
/* ===========================================
ANIMATIONS - Techy Futuristic Style
=========================================== */
.reveal {
opacity: 0;
transform: translateY(30px);
transition: opacity var(--duration-normal) var(--ease-out-expo),
transform var(--duration-normal) var(--ease-out-expo);
}
.slide.visible .reveal {
opacity: 1;
transform: translateY(0);
}
/* Stagger children for sequential reveal */
.reveal:nth-child(1) { transition-delay: 0.1s; }
.reveal:nth-child(2) { transition-delay: 0.2s; }
.reveal:nth-child(3) { transition-delay: 0.3s; }
.reveal:nth-child(4) { transition-delay: 0.4s; }
.reveal:nth-child(5) { transition-delay: 0.5s; }
/* Neon glow effects */
.neon-glow {
text-shadow:
0 0 10px var(--accent-glow),
0 0 20px var(--accent-glow),
0 0 30px var(--accent-glow);
}
/* Code block styling */
.code-block {
background: var(--bg-secondary);
border: 1px solid rgba(0, 255, 204, 0.3);
border-radius: 8px;
padding: 1.5rem;
margin: var(--content-gap) 0;
font-family: var(--font-mono);
font-size: var(--code-size);
position: relative;
overflow-x: auto;
}
.code-block::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 2px;
background: linear-gradient(90deg, var(--accent), var(--accent-purple));
}
/* Card styling */
.card {
background: var(--bg-card);
border: 1px solid rgba(0, 255, 204, 0.2);
border-radius: 12px;
padding: 1.5rem;
backdrop-filter: blur(10px);
margin-bottom: var(--content-gap);
}
/* Feature list */
.feature-list {
list-style: none;
display: flex;
flex-direction: column;
gap: var(--element-gap);
}
.feature-list li {
font-size: var(--body-size);
line-height: 1.5;
display: flex;
align-items: flex-start;
gap: 0.5rem;
}
.feature-list li::before {
content: '▸';
color: var(--accent);
font-weight: bold;
flex-shrink: 0;
}
/* Gradient backgrounds */
.gradient-bg {
background:
radial-gradient(ellipse at 20% 80%, rgba(139, 92, 246, 0.2) 0%, transparent 50%),
radial-gradient(ellipse at 80% 20%, rgba(59, 130, 246, 0.2) 0%, transparent 50%),
var(--bg-primary);
}
/* Circuit lines effect */
.circuit-lines {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
opacity: 0.2;
pointer-events: none;
z-index: 1;
}
.circuit-line {
position: absolute;
background: linear-gradient(90deg, transparent, var(--accent), transparent);
height: 1px;
animation: flow 3s linear infinite;
}
@keyframes flow {
0% { transform: translateX(-100%); }
100% { transform: translateX(100%); }
}
/* Particle system */
.particles {
position: absolute;
width: 100%;
height: 100%;
overflow: hidden;
z-index: 1;
}
.particle {
position: absolute;
width: 3px;
height: 3px;
background: var(--accent);
border-radius: 50%;
opacity: 0;
animation: particle-rise 10s linear infinite;
}
@keyframes particle-rise {
0% {
transform: translateY(100vh) scale(0);
opacity: 0;
}
10% {
opacity: 0.8;
}
90% {
opacity: 0.8;
}
100% {
transform: translateY(-100vh) scale(1.5);
opacity: 0;
}
}
/* Tech grid pattern */
.tech-grid {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-image:
repeating-linear-gradient(0deg, transparent, transparent 40px, rgba(0, 255, 204, 0.03) 40px, rgba(0, 255, 204, 0.03) 41px),
repeating-linear-gradient(90deg, transparent, transparent 40px, rgba(0, 255, 204, 0.03) 40px, rgba(0, 255, 204, 0.03) 41px);
pointer-events: none;
z-index: 1;
}
/* Navigation dots */
.nav-dots {
position: fixed;
bottom: 2rem;
left: 50%;
transform: translateX(-50%);
display: flex;
gap: 0.5rem;
z-index: 1000;
}
.nav-dot {
width: 10px;
height: 10px;
border-radius: 50%;
background: rgba(255, 255, 255, 0.3);
cursor: pointer;
transition: all 0.3s ease;
}
.nav-dot.active {
background: var(--accent);
transform: scale(1.3);
}
/* Progress bar */
.progress-bar {
position: fixed;
top: 0;
left: 0;
height: 3px;
background: var(--accent);
z-index: 1000;
transition: width 0.3s ease;
}
/* Interactive code highlight */
.code-highlight {
color: var(--accent);
font-weight: 600;
}
/* Badge styling */
.badge {
display: inline-block;
background: rgba(0, 255, 204, 0.1);
border: 1px solid var(--accent);
color: var(--accent);
padding: 0.3rem 0.8rem;
border-radius: 20px;
font-size: var(--code-size);
margin-right: 0.5rem;
}
/* Grid layout */
.grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: var(--content-gap);
margin-top: var(--content-gap);
}
/* Two-column layout */
.two-column {
display: grid;
grid-template-columns: 1fr 1fr;
gap: var(--content-gap);
}
/* Keyboard hint */
.keyboard-hint {
position: fixed;
bottom: 1rem;
right: 1rem;
font-size: var(--small-size);
color: var(--text-secondary);
opacity: 0.7;
z-index: 1000;
}
/* Responsive adjustments */
@media (max-height: 700px) {
:root {
--slide-padding: clamp(1rem, 3vw, 2rem);
--content-gap: clamp(0.8rem, 1.5vw, 1.5rem);
}
}
@media (max-width: 768px) {
.two-column {
grid-template-columns: 1fr;
}
}
/* Inline editing styles (if enabled) */
.edit-hotzone {
position: fixed;
top: 0;
left: 0;
width: 80px;
height: 80px;
z-index: 10000;
cursor: pointer;
}
.edit-toggle {
position: fixed;
top: 1rem;
left: 1rem;
width: 40px;
height: 40px;
border-radius: 50%;
background: var(--accent);
border: none;
color: var(--bg-primary);
font-size: 1.2rem;
cursor: pointer;
opacity: 0;
pointer-events: none;
transition: opacity 0.3s ease;
z-index: 10001;
display: flex;
align-items: center;
justify-content: center;
}
.edit-toggle.show,
.edit-toggle.active {
opacity: 1;
pointer-events: auto;
}
.contenteditable {
cursor: text;
padding: 0.2rem 0.4rem;
border-radius: 4px;
transition: background 0.3s ease;
}
.contenteditable:hover {
background: rgba(255, 255, 255, 0.05);
}
.contenteditable:focus {
outline: 2px solid var(--accent);
background: rgba(255, 255, 255, 0.1);
}
/* Prefers reduced motion */
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
transition-duration: 0.2s !important;
}
}
</style>
</head>
<body>
<!-- Progress bar -->
<div class="progress-bar" id="progressBar"></div>
<!-- Navigation dots -->
<nav class="nav-dots" id="navDots"></nav>
<!-- Inline editing toggle -->
<div class="edit-hotzone" id="editHotzone"></div>
<button class="edit-toggle" id="editToggle" title="编辑模式 (E)">✏️</button>
<!-- Background effects -->
<div class="tech-grid"></div>
<div class="circuit-lines">
<div class="circuit-line" style="top: 20%; width: 100%; animation-delay: 0s;"></div>
<div class="circuit-line" style="top: 50%; width: 100%; animation-delay: 1s;"></div>
<div class="circuit-line" style="top: 80%; width: 100%; animation-delay: 2s;"></div>
</div>
<div class="particles" id="particles"></div>
<!-- Slides -->
<!-- Slide 1: Title -->
<section class="slide gradient-bg">
<div class="slide-content">
<div class="reveal">
<div class="badge">C++ 编程进阶</div>
<h1 class="neon-glow">CSP03-07</h1>
<h2>string 使用技巧与回文判断</h2>
</div>
</div>
</section>
<!-- Slide 2: Course Overview -->
<section class="slide gradient-bg">
<div class="slide-content">
<h2 class="reveal">课程目标</h2>
<ul class="feature-list reveal">
<li>掌握 <span class="code-highlight">string</span> 的子串提取(<code>substr</code>)操作</li>
<li>掌握字符串回文、对称的判断方法</li>
<li>综合运用 <code>string</code> 的各种操作解决复杂字符串问题</li>
</ul>
<h2 class="reveal" style="margin-top: 2rem;">核心知识点</h2>
<ul class="feature-list reveal">
<li><code>s.substr(pos, len)</code>:提取从 pos 开始长度为 len 的子串</li>
<li><code>s.substr(pos)</code>:提取从 pos 到末尾的子串</li>
<li>字符串对称(回文)的判断方法:双指针、逆序比较</li>
<li>string 综合应用:字符串处理的整体思路</li>
</ul>
</div>
</section>
<!-- Slide 3: Knowledge Review -->
<section class="slide gradient-bg">
<div class="slide-content">
<div class="card reveal">
<h3>👩‍🏫 教师引导</h3>
<p>上节课我们学了 string 的基础:拼接、搜索、替换、大小写转换。</p>
<p>今天继续进阶——学习如何"切割"字符串(提取子串),以及一个经典问题:<strong>回文判断</strong></p>
<p>最后,我们用综合题来验证这两节课学到的所有 string 技能!</p>
</div>
<div class="card reveal">
<h3>💡 互动复习</h3>
<ul class="feature-list">
<li><code>s.find("abc")</code> 找不到时返回什么?(<code>string::npos</code></li>
<li>如何把字符串 <code>s</code> 全部转成大写for 循环 + toupper</li>
</ul>
</div>
</div>
</section>
<!-- Slide 4: substr Introduction -->
<section class="slide gradient-bg">
<div class="slide-content">
<h2 class="reveal">🎬 新知导入:提取子串</h2>
<div class="card reveal">
<p>假设你有一个身份证号码字符串:<code>"110105199001234567"</code></p>
<p>你想从中提取出生年月日第7到第14位怎么做</p>
<p style="color: var(--accent); font-size: 1.2rem; font-weight: 600;"><code>s.substr(6, 8)</code> 就能一次搞定!</p>
<p style="font-size: 0.9rem; opacity: 0.8; margin-top: 0.5rem;">下标从0开始第7位就是下标6</p>
</div>
<p class="reveal" style="margin-top: 2rem; text-align: center; opacity: 0.8;">✨ 这就是 <code>substr</code> 的魔力——精准"切割"字符串的任意一段!</p>
</div>
</section>
<!-- Slide 5: substr Methods -->
<section class="slide gradient-bg">
<div class="slide-content">
<h2 class="reveal">提取子串 substr</h2>
<div class="code-block reveal">
<pre><code>string s = "hello world";
// 格式1s.substr(pos, len) — 从 pos 开始,截取 len 个字符
string sub1 = s.substr(0, 5); // "hello"
string sub2 = s.substr(6, 5); // "world"
// 格式2s.substr(pos) — 从 pos 开始截到末尾
string sub3 = s.substr(6); // "world"</code></pre>
</div>
<h3 class="reveal">常见应用</h3>
<div class="code-block reveal">
<pre><code>string date = "2024-03-15";
string year = date.substr(0, 4); // "2024"
string month = date.substr(5, 2); // "03"
string day = date.substr(8, 2); // "15"</code></pre>
</div>
<p class="reveal" style="color: #ff6b6b;">⚠️ 注意:<code>pos</code> 超出字符串长度会报错!使用前要确认 <code>pos < s.size()</code></p>
</div>
</section>
<!-- Slide 6: Palindrome Method 1 -->
<section class="slide gradient-bg">
<div class="slide-content">
<h2 class="reveal">字符串对称(回文)判断</h2>
<h3 class="reveal">方法一:双指针(推荐)</h3>
<div class="code-block reveal">
<pre><code>string s = "racecar";
int l = 0, r = s.size() - 1;
bool isOk = true;
while (l < r) {
if (s[l] != s[r]) {
isOk = false;
break;
}
l++; r--;
}
cout << (isOk ? "YES" : "NO") << endl;</code></pre>
</div>
<div class="card reveal">
<p>🔍 <strong>双指针原理:</strong>分别从字符串的两端向中间移动,比较对应位置的字符是否相同。</p>
</div>
</div>
</section>
<!-- Slide 7: Palindrome Method 2 -->
<section class="slide gradient-bg">
<div class="slide-content">
<h3 class="reveal">方法二:逆序字符串比较</h3>
<div class="code-block reveal">
<pre><code>string s = "racecar";
string rev = s;
reverse(rev.begin(), rev.end()); // 需要 #include &lt;algorithm&gt;
cout << (s == rev ? "YES" : "NO") &lt;&lt; endl;</code></pre>
</div>
<div class="card reveal">
<p>💡 <code>reverse(s.begin(), s.end())</code> 可以直接将 string 原地逆序!</p>
</div>
<div class="two-column">
<div class="card reveal">
<h4>双指针优点</h4>
<ul class="feature-list">
<li>空间复杂度 O(1)</li>
<li>时间复杂度 O(n/2)</li>
<li>提前终止</li>
</ul>
</div>
<div class="card reveal">
<h4>逆序比较优点</h4>
<ul class="feature-list">
<li>代码简洁</li>
<li>易于理解</li>
<li>适用于复杂场景</li>
</ul>
</div>
</div>
</div>
</section>
<!-- Slide 8: Iterators -->
<section class="slide gradient-bg">
<div class="slide-content">
<h2 class="reveal">在 string 中使用 begin/end 迭代器</h2>
<div class="card reveal">
<p><code>string</code> 支持 C++ 标准库算法,可以使用迭代器范围:</p>
</div>
<div class="code-block reveal">
<pre><code>#include &lt;algorithm&gt;
string s = "hello";
reverse(s.begin(), s.end()); // s = "olleh"
sort(s.begin(), s.end()); // s = "ehllo"(排序字符)</code></pre>
</div>
<div class="two-column">
<div class="card reveal">
<h4>常用算法</h4>
<ul class="feature-list">
<li><code>sort()</code> - 排序</li>
<li><code>reverse()</code> - 逆序</li>
<li><code>find()</code> - 查找</li>
</ul>
</div>
<div class="card reveal">
<h4>高级算法</h4>
<ul class="feature-list">
<li><code>count()</code> - 计数</li>
<li><code>replace()</code> - 替换</li>
<li><code>transform()</code> - 转换</li>
</ul>
</div>
</div>
</div>
</section>
<!-- Slide 9: Comprehensive Skills -->
<section class="slide gradient-bg">
<div class="slide-content">
<h2 class="reveal">字符串综合操作技巧</h2>
<div class="card reveal">
<h4>删除字符串中所有指定字符</h4>
<div class="code-block">
<pre><code>string s = "a1b2c3";
string result = "";
for (char c : s) {
if (!isdigit(c)) result += c; // 只保留非数字
}
// result = "abc"</code></pre>
</div>
</div>
<div class="card reveal">
<h4>字符串反转每个单词</h4>
<p>思路:<strong>分割 → 反转每个单词 → 重新拼接</strong></p>
<div class="code-block">
<pre><code>string line = "hello world";
// 使用字符串流分割单词
istringstream ss(line);
string word;
while (ss >> word) {
reverse(word.begin(), word.end());
// ... 重新拼接
}</code></pre>
</div>
</div>
</div>
</section>
<!-- Slide 10: Quick Questions -->
<section class="slide gradient-bg">
<div class="slide-content">
<h2 class="reveal">⚡ GESP 真题演练</h2>
<div class="card reveal">
<h3>抢答题 1选择题</h3>
<p><code>string s = "abcdef"; cout << s.substr(2, 3);</code> 的输出是?</p>
<div style="display: flex; gap: 1rem; margin: 1rem 0;">
<span style="padding: 0.5rem 1rem; background: rgba(255,255,255,0.1); border-radius: 8px;">A. "abc"</span>
<span style="padding: 0.5rem 1rem; background: rgba(0,255,204,0.2); border: 1px solid var(--accent); border-radius: 8px;">B. "bcd"</span>
<span style="padding: 0.5rem 1rem; background: rgba(255,255,255,0.1); border-radius: 8px;">C. "cde"</span>
<span style="padding: 0.5rem 1rem; background: rgba(255,255,255,0.1); border-radius: 8px;">D. "def"</span>
</div>
<p><strong>答案C</strong> | 解析:从下标 2字符 'c')开始,截取 3 个字符:"cde"。</p>
</div>
<div class="card reveal">
<h3>判断题 2</h3>
<p>判断回文串,可以将字符串逆序后与原字符串比较,相同则是回文。( </p>
<p style="color: var(--accent); font-weight: 600; font-size: 1.2rem;">✓(正确)</p>
</div>
</div>
</section>
<!-- Slide 11: Practice 1 -->
<section class="slide gradient-bg">
<div class="slide-content">
<h2 class="reveal">🎈 课堂练习:提取子串</h2>
<div class="card reveal">
<h3>题目描述</h3>
<p>输入字符串 s 和两个整数 l、r1-indexed输出 s 的第 l 到第 r 个字符(含边界)。</p>
<h3>输入格式</h3>
<p>第一行字符串 s不含空格<br>第二行两整数 l r</p>
<h3>输出格式</h3>
<p>提取的子串</p>
<h3>样例</h3>
<div style="background: rgba(0,0,0,0.2); padding: 1rem; border-radius: 8px;">
<p><strong>输入:</strong></p>
<pre>helloworld
2 6</pre>
<p><strong>输出:</strong></p>
<pre style="color: var(--accent);">ellow</pre>
</div>
</div>
</div>
</section>
<!-- Slide 12: Practice 2 -->
<section class="slide gradient-bg">
<div class="slide-content">
<h2 class="reveal">🎈 课堂练习:判断回文串</h2>
<div class="card reveal">
<h3>题目描述</h3>
<p>输入一个字符串,判断它是否是回文串(忽略大小写)。</p>
<h3>输入格式</h3>
<p>一行字符串(不含空格,长度 ≤ 1000</p>
<h3>输出格式</h3>
<p><code>YES</code><code>NO</code></p>
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 2rem;">
<div>
<h3>样例输入</h3>
<div style="background: rgba(0,0,0,0.2); padding: 1rem; border-radius: 8px;">
<pre>Racecar</pre>
</div>
</div>
<div>
<h3>样例输出</h3>
<div style="background: rgba(0,255,204,0.2); padding: 1rem; border-radius: 8px; color: var(--accent);">
<pre>YES</pre>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Slide 13: Advanced Practice -->
<section class="slide gradient-bg">
<div class="slide-content">
<h2 class="reveal">🔥 进阶练习:最长回文子串</h2>
<div class="card reveal">
<h3>题目描述</h3>
<p>给定一个字符串 s只含小写字母求其中最长的回文子串。如有多个长度相同的输出字典序最小的。</p>
<h3>输入格式</h3>
<p>一行字符串 s长度 ≤ 500</p>
<h3>输出格式</h3>
<p>最长回文子串</p>
<div style="background: rgba(0,0,0,0.2); padding: 1rem; border-radius: 8px;">
<p><strong>样例输入:</strong> babad</p>
<p><strong>样例输出:</strong> <span style="color: var(--accent); font-weight: 600;">aba</span></p>
</div>
</div>
<div class="card reveal" style="margin-top: 1rem;">
<h3>📌 算法思路</h3>
<p>枚举所有起始位置 i 和长度 len提取子串判断是否回文。</p>
<p>时间复杂度 O(n²),对 n≤1000 的字符串完全足够。</p>
</div>
</div>
</section>
<!-- Slide 14: Summary -->
<section class="slide gradient-bg">
<div class="slide-content">
<h2 class="reveal">🌟 课堂总结</h2>
<div class="card reveal">
<h3>今天完成了 string 的进阶学习!</h3>
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; margin: 1rem 0;">
<div style="text-align: center; padding: 1rem;">
<div style="font-size: 2rem; margin-bottom: 0.5rem;">🔤</div>
<code style="font-size: 1.1rem;">s.substr(pos, len)</code>
<p style="font-size: 0.9rem; margin-top: 0.5rem;">精准提取子串</p>
</div>
<div style="text-align: center; padding: 1rem;">
<div style="font-size: 2rem; margin-bottom: 0.5rem;">🔄</div>
<code style="font-size: 1.1rem;">reverse()</code>
<p style="font-size: 0.9rem; margin-top: 0.5rem;">原地逆序</p>
</div>
</div>
<p style="text-align: center; margin: 1.5rem 0; font-size: 1.2rem;">✨ 这两节课加起来string 的重要操作已经全部掌握!</p>
<div style="text-align: center; background: rgba(0,255,204,0.1); padding: 1rem; border-radius: 8px;">
<p>下节课开始进入全新主题:<strong>算法篇</strong></p>
<p>从"枚举算法"开始,学习信息学竞赛的思维方法!</p>
</div>
</div>
</div>
</section>
<!-- Slide 15: Homework -->
<section class="slide gradient-bg">
<div class="slide-content">
<h2 class="reveal">📝 课后作业</h2>
<div class="card reveal">
<h3>作业 1提取域名</h3>
<p>输入一个 URL提取其中的域名在 "://" 之后到 "/" 或末尾之前)。</p>
<p style="background: rgba(0,0,0,0.2); padding: 0.5rem; border-radius: 4px; margin: 0.5rem 0;">
<strong>样例:</strong> https://www.example.com/page → www.example.com
</p>
</div>
<div class="card reveal">
<h3>作业 2字符串旋转</h3>
<p>输入字符串 s 和整数 k将 s 向左旋转 k 位(前 k 字符移到末尾)。</p>
<p style="background: rgba(0,0,0,0.2); padding: 0.5rem; border-radius: 4px; margin: 0.5rem 0;">
<strong>样例:</strong> abcde, 2 → cdeab
</p>
</div>
<div class="card reveal">
<h3>作业 3统计回文子串个数</h3>
<p>输入字符串 s统计其中长度 ≥ 2 的回文子串的数量。</p>
<p style="background: rgba(0,0,0,0.2); padding: 0.5rem; border-radius: 4px; margin: 0.5rem 0;">
<strong>样例:</strong> aaa → 3"aa"×2 + "aaa"×1
</p>
</div>
</div>
</section>
<!-- Keyboard hint -->
<div class="keyboard-hint">← → 空格键导航</div>
<script>
/* ===========================================
SLIDE PRESENTATION CONTROLLER
=========================================== */
class SlidePresentation {
constructor() {
this.slides = document.querySelectorAll('.slide');
this.currentSlide = 0;
this.isAnimating = false;
this.setupParticles();
this.setupIntersectionObserver();
this.setupKeyboardNav();
this.setupTouchNav();
this.setupProgressBar();
this.setupNavDots();
this.setupInlineEditing();
}
setupParticles() {
const particlesContainer = document.getElementById('particles');
for (let i = 0; i < 30; i++) {
const particle = document.createElement('div');
particle.className = 'particle';
particle.style.left = Math.random() * 100 + '%';
particle.style.animationDelay = Math.random() * 10 + 's';
particle.style.animationDuration = (10 + Math.random() * 10) + 's';
particlesContainer.appendChild(particle);
}
}
setupIntersectionObserver() {
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('visible');
}
});
}, { threshold: 0.1 });
this.slides.forEach(slide => {
observer.observe(slide);
});
}
setupKeyboardNav() {
document.addEventListener('keydown', (e) => {
if (this.isAnimating) return;
switch(e.key) {
case 'ArrowLeft':
case 'ArrowUp':
e.preventDefault();
this.prevSlide();
break;
case 'ArrowRight':
case 'ArrowDown':
case ' ':
case 'PageDown':
e.preventDefault();
this.nextSlide();
break;
case 'PageUp':
e.preventDefault();
this.prevSlide();
break;
case 'Home':
e.preventDefault();
this.goToSlide(0);
break;
case 'End':
e.preventDefault();
this.goToSlide(this.slides.length - 1);
break;
}
});
}
setupTouchNav() {
let touchStartY = 0;
let touchEndY = 0;
document.addEventListener('touchstart', (e) => {
touchStartY = e.changedTouches[0].screenY;
});
document.addEventListener('touchend', (e) => {
touchEndY = e.changedTouches[0].screenY;
this.handleSwipe(touchStartY, touchEndY);
});
}
handleSwipe(startY, endY) {
const swipeThreshold = 50;
const diff = startY - endY;
if (Math.abs(diff) > swipeThreshold) {
if (diff > 0) {
// Swipe up - next slide
this.nextSlide();
} else {
// Swipe down - prev slide
this.prevSlide();
}
}
}
setupProgressBar() {
const progressBar = document.getElementById('progressBar');
const updateProgress = () => {
const progress = ((this.currentSlide + 1) / this.slides.length) * 100;
progressBar.style.width = `${progress}%`;
};
updateProgress();
}
setupNavDots() {
const navDots = document.getElementById('navDots');
this.slides.forEach((_, index) => {
const dot = document.createElement('div');
dot.className = 'nav-dot';
if (index === 0) dot.classList.add('active');
dot.addEventListener('click', () => {
this.goToSlide(index);
});
navDots.appendChild(dot);
});
// Update active dot on slide change
const updateActiveDot = () => {
document.querySelectorAll('.nav-dot').forEach((dot, index) => {
dot.classList.toggle('active', index === this.currentSlide);
});
};
// Store update method
this.updateActiveDot = updateActiveDot;
this.updateActiveDot();
}
setupInlineEditing() {
if (!localStorage.getItem('enableInlineEditing') === 'true') return;
const editor = new InlineEditor();
window.editor = editor; // For debugging
}
nextSlide() {
if (this.currentSlide < this.slides.length - 1) {
this.goToSlide(this.currentSlide + 1);
}
}
prevSlide() {
if (this.currentSlide > 0) {
this.goToSlide(this.currentSlide - 1);
}
}
goToSlide(index) {
if (index < 0 || index >= this.slides.length || index === this.currentSlide) return;
this.isAnimating = true;
this.slides[this.currentSlide].scrollIntoView({ behavior: 'smooth' });
this.currentSlide = index;
this.updateActiveDot();
setTimeout(() => {
this.isAnimating = false;
}, 600);
}
}
/* ===========================================
INLINE EDITOR (Opt-in only)
=========================================== */
class InlineEditor {
constructor() {
this.isActive = false;
this.editToggle = document.getElementById('editToggle');
this.editHotzone = document.getElementById('editHotzone');
this.setupHotzone();
this.setupToggle();
this.setupKeyboardShortcut();
}
setupHotzone() {
let hideTimeout = null;
const showToggle = () => {
clearTimeout(hideTimeout);
this.editToggle.classList.add('show');
};
const hideToggle = () => {
hideTimeout = setTimeout(() => {
if (!this.isActive) this.editToggle.classList.remove('show');
}, 400);
};
this.editHotzone.addEventListener('mouseenter', showToggle);
this.editHotzone.addEventListener('mouseleave', hideToggle);
this.editToggle.addEventListener('mouseenter', showToggle);
this.editToggle.addEventListener('mouseleave', hideToggle);
this.editHotzone.addEventListener('click', () => this.toggleEditMode());
}
setupToggle() {
this.editToggle.addEventListener('click', () => this.toggleEditMode());
}
setupKeyboardShortcut() {
document.addEventListener('keydown', (e) => {
if ((e.key === 'e' || e.key === 'E') && !e.target.getAttribute('contenteditable')) {
this.toggleEditMode();
}
});
}
toggleEditMode() {
this.isActive = !this.isActive;
this.editToggle.classList.toggle('active', this.isActive);
if (this.isActive) {
this.enableEditing();
} else {
this.disableEditing();
}
}
enableEditing() {
document.querySelectorAll('h1, h2, h3, p, .card').forEach(element => {
if (!element.querySelector('code')) { // Don't edit code blocks
element.contentEditable = true;
element.classList.add('contenteditable');
}
});
// Auto-save on input
document.addEventListener('input', this.saveToStorage.bind(this));
this.loadFromStorage();
}
disableEditing() {
document.querySelectorAll('.contenteditable').forEach(element => {
element.contentEditable = false;
element.classList.remove('contenteditable');
});
// Clean up
document.removeEventListener('input', this.saveToStorage.bind(this));
this.saveToStorage();
}
saveToStorage() {
const content = {};
document.querySelectorAll('h1, h2, h3, p').forEach((element, index) => {
if (element.classList.contains('contenteditable')) {
content[index] = element.innerHTML;
}
});
localStorage.setItem('slideContent', JSON.stringify(content));
}
loadFromStorage() {
const saved = localStorage.getItem('slideContent');
if (saved) {
const content = JSON.parse(saved);
Object.entries(content).forEach(([index, html]) => {
const element = document.querySelector(`.contenteditable:nth-child(${parseInt(index) + 1})`);
if (element) element.innerHTML = html;
});
}
}
}
/* Initialize presentation */
const presentation = new SlidePresentation();
</script>
</body>
</html>