1172 lines
55 KiB
Python
1172 lines
55 KiB
Python
"""Dynamic Programming algorithm JS generators for generate.py"""
|
||
|
||
def js_climbing_stairs():
|
||
return r'''
|
||
const examples = [
|
||
{input: 5, label: '示例1: n=5'},
|
||
{input: 3, label: '示例2: n=3'},
|
||
{input: 10, label: '示例3: n=10'},
|
||
];
|
||
let steps, stepCtrl, n;
|
||
|
||
function buildSteps(total) {
|
||
n = total; steps = [];
|
||
const dp = new Array(n + 1).fill(0);
|
||
dp[0] = 0; dp[1] = 1; dp[2] = 2;
|
||
steps.push({stage:'init', msg:'初始化:dp[1]=1(1种),dp[2]=2(2种)', dp:[...dp], current:-1, from1:-1, from2:-1});
|
||
for (let i = 3; i <= n; i++) {
|
||
steps.push({stage:'calc', msg:`计算 dp[${i}] = dp[${i-1}] + dp[${i-2}] = ${dp[i-1]} + ${dp[i-2]} = ${dp[i-1]+dp[i-2]}`, dp:[...dp], current:i, from1:i-1, from2:i-2});
|
||
dp[i] = dp[i-1] + dp[i-2];
|
||
steps.push({stage:'fill', msg:`dp[${i}] = ${dp[i]},已填充`, dp:[...dp], current:i, from1:i-1, from2:i-2});
|
||
}
|
||
steps.push({stage:'done', msg:`结果:爬到第 ${n} 阶有 ${dp[n]} 种方法`, dp:[...dp], current:n, from1:-1, from2:-1});
|
||
}
|
||
|
||
function render(step) {
|
||
const s = steps[step];
|
||
let viz = '<div style="display:flex;align-items:flex-end;gap:3px;margin:12px 0 8px;">';
|
||
for (let i = 1; i <= n; i++) {
|
||
const filled = s.dp[i] > 0;
|
||
const isCurrent = i === s.current;
|
||
const isFrom1 = i === s.from1;
|
||
const isFrom2 = i === s.from2;
|
||
let bg = '#e2e8f0', color = '#64748b';
|
||
if (isCurrent && (s.stage==='fill'||s.stage==='done')) { bg = 'var(--green)'; color = 'white'; }
|
||
else if (isCurrent) { bg = 'var(--orange)'; color = 'white'; }
|
||
else if (isFrom1) { bg = '#dbeafe'; color = '#1e40af'; }
|
||
else if (isFrom2) { bg = '#ede9fe'; color = '#5b21b6'; }
|
||
else if (filled) { bg = '#f0fdf4'; color = '#166534'; }
|
||
viz += `<div style="width:42px;height:${36+i*6}px;background:${bg};border-radius:6px 6px 0 0;display:flex;flex-direction:column;align-items:center;justify-content:flex-end;padding:4px 0;font-size:11px;font-weight:600;color:${color};transition:all 0.3s;">
|
||
<span style="font-size:13px;">${s.dp[i]||''}</span><span style="font-size:9px;opacity:0.7;">${i}</span></div>`;
|
||
}
|
||
viz += '</div>';
|
||
viz += '<div style="margin-top:12px;"><b>dp 表:</b></div>';
|
||
const dpHl = {};
|
||
if (s.current > 0) dpHl[s.current-1] = s.stage==='fill'?'green':'orange';
|
||
if (s.from1 > 0) dpHl[s.from1-1] = 'blue';
|
||
if (s.from2 > 0) dpHl[s.from2-1] = 'purple';
|
||
viz += renderArray(s.dp.slice(1), {highlights: dpHl});
|
||
if (s.stage === 'calc') {
|
||
viz += `<div class="formula-box" style="margin-top:8px;">dp[${s.current}] = dp[${s.from1}] + dp[${s.from2}] = ${s.dp[s.from1]} + ${s.dp[s.from2]}</div>`;
|
||
}
|
||
$('vizArea').innerHTML = viz;
|
||
|
||
$('detailContent').innerHTML = '<div class="calc-block">' + s.msg + '</div>';
|
||
if (s.stage !== 'done' && s.current > 0) {
|
||
$('detailContent').innerHTML += `<div class="current-answer">dp[${s.current}] = <b>${s.dp[s.current]}</b></div>`;
|
||
}
|
||
if (s.stage === 'done') {
|
||
$('resultContent').innerHTML = `<div class="final-answer">爬到第 ${n} 阶有 <b>${s.dp[n]}</b> 种方法</div>`;
|
||
}
|
||
$('hintText').textContent = s.msg;
|
||
const stages = ['init→初始化','calc→递推计算','fill→填充','done→完成'];
|
||
$('pipeline').innerHTML = stages.map(st => {
|
||
const [k,l] = st.split('→');
|
||
return `<span class="pipe-step ${s.stage===k?'active':''}">${l}</span>`;
|
||
}).join('<i>→</i>');
|
||
}
|
||
|
||
function init() {
|
||
const sel = $('exampleSelect');
|
||
examples.forEach((e,i) => { sel.innerHTML += `<option value="${i}">${e.label}</option>`; });
|
||
$('inputArea').value = 'n=5';
|
||
buildSteps(examples[0].input);
|
||
stepCtrl = new StepController({onStep: render});
|
||
stepCtrl.setSteps(steps.map((_,i)=>i));
|
||
$('stepInfo').textContent = '步骤 1 / ' + steps.length;
|
||
stepCtrl.onStep = (idx) => { render(idx); $('stepInfo').textContent = `步骤 ${idx+1} / ${steps.length}`; };
|
||
$('applyBtn').onclick = () => {
|
||
const m = $('inputArea').value.match(/n=(\d+)/);
|
||
if (!m) { alert('格式: n=5'); return; }
|
||
buildSteps(parseInt(m[1]));
|
||
stepCtrl.setSteps(steps.map((_,i)=>i)); render(0); $('stepInfo').textContent = '步骤 1 / ' + steps.length;
|
||
};
|
||
$('exampleSelect').onchange = () => {
|
||
const e = examples[parseInt($('exampleSelect').value)];
|
||
$('inputArea').value = `n=${e.input}`;
|
||
buildSteps(e.input); stepCtrl.setSteps(steps.map((_,i)=>i)); render(0);
|
||
};
|
||
$('prevBtn').onclick = () => stepCtrl.prev();
|
||
$('nextBtn').onclick = () => stepCtrl.next();
|
||
$('jumpBtn').onclick = () => stepCtrl.jumpToEnd();
|
||
$('autoBtn').onclick = () => { const on = stepCtrl.toggleAuto(); $('autoBtn').textContent = on ? '暂停' : '自动播放'; };
|
||
$('resetBtn').onclick = () => { stepCtrl.reset(); $('autoBtn').textContent = '自动播放'; };
|
||
}
|
||
init();
|
||
$('codeArea').innerHTML = renderCode(`def climbStairs(n):
|
||
if n <= 2: return n
|
||
dp = [0] * (n + 1)
|
||
dp[1], dp[2] = 1, 2
|
||
for i in range(3, n + 1):
|
||
dp[i] = dp[i-1] + dp[i-2]
|
||
return dp[n]`, {lang:'Python'});
|
||
'''
|
||
|
||
|
||
def js_pascals_triangle():
|
||
return r'''
|
||
const examples = [
|
||
{input: 5, label: '示例1: numRows=5'},
|
||
{input: 1, label: '示例2: numRows=1'},
|
||
{input: 7, label: '示例3: numRows=7'},
|
||
];
|
||
let steps, stepCtrl, numRows;
|
||
|
||
function buildSteps(n) {
|
||
numRows = n; steps = [];
|
||
const triangle = [];
|
||
steps.push({stage:'init', msg:'开始生成杨辉三角,每行首尾为1,中间元素 = 上方两数之和', triangle:[], row:-1, col:-1});
|
||
for (let i = 0; i < numRows; i++) {
|
||
const row = new Array(i + 1).fill(1);
|
||
steps.push({stage:'newRow', msg:`第 ${i} 行共 ${i+1} 个元素,首尾均为 1`, triangle:JSON.parse(JSON.stringify(triangle)), row:i, col:-1});
|
||
for (let j = 1; j < i; j++) {
|
||
row[j] = triangle[i-1][j-1] + triangle[i-1][j];
|
||
steps.push({stage:'calc', msg:`triangle[${i}][${j}] = triangle[${i-1}][${j-1}] + triangle[${i-1}][${j}] = ${triangle[i-1][j-1]} + ${triangle[i-1][j]} = ${row[j]}`, triangle:JSON.parse(JSON.stringify(triangle)), row:i, col:j});
|
||
}
|
||
triangle.push([...row]);
|
||
steps.push({stage:'fillRow', msg:`第 ${i} 行填充完毕:[${row.join(', ')}]`, triangle:JSON.parse(JSON.stringify(triangle)), row:i, col:i});
|
||
}
|
||
steps.push({stage:'done', msg:`杨辉三角生成完毕,共 ${numRows} 行`, triangle:JSON.parse(JSON.stringify(triangle)), row:-1, col:-1});
|
||
}
|
||
|
||
function render(step) {
|
||
const s = steps[step];
|
||
let viz = '<div style="display:flex;flex-direction:column;align-items:center;gap:4px;padding:12px 0;">';
|
||
const maxRow = s.triangle.length;
|
||
for (let i = 0; i < maxRow; i++) {
|
||
const gap = Math.max(2, (maxRow - i) * 6);
|
||
viz += `<div style="display:flex;gap:${gap}px;justify-content:center;">`;
|
||
for (let j = 0; j <= i; j++) {
|
||
const val = s.triangle[i][j];
|
||
let cls = 'default';
|
||
if (i === s.row && s.stage !== 'done') {
|
||
cls = j === s.col ? 'orange' : 'green';
|
||
}
|
||
if (s.stage === 'calc' && i === s.row - 1 && (j === s.col - 1 || j === s.col)) {
|
||
cls = 'blue';
|
||
}
|
||
viz += `<span class="chip ${cls}" style="min-width:32px;height:28px;font-size:13px;">${val}</span>`;
|
||
}
|
||
viz += '</div>';
|
||
}
|
||
if (s.stage === 'newRow' || s.stage === 'calc') {
|
||
viz += `<div style="display:flex;gap:2px;justify-content:center;margin-top:2px;">`;
|
||
for (let j = 0; j <= s.row; j++) {
|
||
viz += `<span style="min-width:32px;height:28px;border:2px dashed var(--orange);border-radius:10px;display:flex;align-items:center;justify-content:center;font-size:13px;color:var(--text-muted);">?</span>`;
|
||
}
|
||
viz += '</div>';
|
||
}
|
||
viz += '</div>';
|
||
$('vizArea').innerHTML = viz;
|
||
|
||
let detail = '<div class="calc-block">' + s.msg + '</div>';
|
||
if (s.triangle.length > 0) {
|
||
detail += '<div style="margin-top:8px;"><b>当前三角:</b></div>';
|
||
s.triangle.forEach((row, i) => {
|
||
detail += `<div style="margin:2px 0;"><code>${i}: [${row.join(', ')}]</code></div>`;
|
||
});
|
||
}
|
||
$('detailContent').innerHTML = detail;
|
||
|
||
if (s.stage === 'done') {
|
||
$('resultContent').innerHTML = `<div class="final-answer">杨辉三角 <b>${numRows}</b> 行已生成</div>`;
|
||
}
|
||
$('hintText').textContent = s.msg;
|
||
const stages = ['init→开始','newRow→新行','calc→计算','fillRow→填充','done→完成'];
|
||
$('pipeline').innerHTML = stages.map(st => {
|
||
const [k,l] = st.split('→');
|
||
return `<span class="pipe-step ${s.stage===k?'active':''}">${l}</span>`;
|
||
}).join('<i>→</i>');
|
||
}
|
||
|
||
function init() {
|
||
const sel = $('exampleSelect');
|
||
examples.forEach((e,i) => { sel.innerHTML += `<option value="${i}">${e.label}</option>`; });
|
||
$('inputArea').value = 'numRows=5';
|
||
buildSteps(examples[0].input);
|
||
stepCtrl = new StepController({onStep: render});
|
||
stepCtrl.setSteps(steps.map((_,i)=>i));
|
||
$('stepInfo').textContent = '步骤 1 / ' + steps.length;
|
||
stepCtrl.onStep = (idx) => { render(idx); $('stepInfo').textContent = `步骤 ${idx+1} / ${steps.length}`; };
|
||
$('applyBtn').onclick = () => {
|
||
const m = $('inputArea').value.match(/numRows=(\d+)/);
|
||
if (!m) { alert('格式: numRows=5'); return; }
|
||
buildSteps(parseInt(m[1]));
|
||
stepCtrl.setSteps(steps.map((_,i)=>i)); render(0); $('stepInfo').textContent = '步骤 1 / ' + steps.length;
|
||
};
|
||
$('exampleSelect').onchange = () => {
|
||
const e = examples[parseInt($('exampleSelect').value)];
|
||
$('inputArea').value = `numRows=${e.input}`;
|
||
buildSteps(e.input); stepCtrl.setSteps(steps.map((_,i)=>i)); render(0);
|
||
};
|
||
$('prevBtn').onclick = () => stepCtrl.prev();
|
||
$('nextBtn').onclick = () => stepCtrl.next();
|
||
$('jumpBtn').onclick = () => stepCtrl.jumpToEnd();
|
||
$('autoBtn').onclick = () => { const on = stepCtrl.toggleAuto(); $('autoBtn').textContent = on ? '暂停' : '自动播放'; };
|
||
$('resetBtn').onclick = () => { stepCtrl.reset(); $('autoBtn').textContent = '自动播放'; };
|
||
}
|
||
init();
|
||
$('codeArea').innerHTML = renderCode(`def generate(numRows):
|
||
triangle = []
|
||
for i in range(numRows):
|
||
row = [1] * (i + 1)
|
||
for j in range(1, i):
|
||
row[j] = triangle[i-1][j-1] + triangle[i-1][j]
|
||
triangle.append(row)
|
||
return triangle`, {lang:'Python'});
|
||
'''
|
||
|
||
|
||
def js_house_robber():
|
||
return r'''
|
||
const examples = [
|
||
{input: [1,2,3,1], label: '示例1: [1,2,3,1]'},
|
||
{input: [2,7,9,3,1], label: '示例2: [2,7,9,3,1]'},
|
||
{input: [2,1,1,2], label: '示例3: [2,1,1,2]'},
|
||
];
|
||
let nums, steps, stepCtrl;
|
||
|
||
function buildSteps(arr) {
|
||
nums = [...arr]; steps = [];
|
||
const n = nums.length;
|
||
if (n === 0) { steps.push({stage:'done', msg:'数组为空', dp:[], rob:[]}); return; }
|
||
const dp = new Array(n).fill(0);
|
||
const rob = new Array(n).fill(false);
|
||
dp[0] = nums[0]; rob[0] = true;
|
||
steps.push({stage:'init', msg:`dp[0] = ${nums[0]}(只有一间房子,必须偷)`, dp:[...dp], rob:[...rob], current:0, choice:null});
|
||
if (n > 1) {
|
||
if (nums[1] > nums[0]) { dp[1] = nums[1]; rob[1] = true; rob[0] = false; }
|
||
else { dp[1] = nums[0]; rob[1] = false; }
|
||
steps.push({stage:'init2', msg:`dp[1] = max(${nums[0]}, ${nums[1]}) = ${dp[1]}`, dp:[...dp], rob:[...rob], current:1, choice:'max'});
|
||
}
|
||
for (let i = 2; i < n; i++) {
|
||
const notRob = dp[i-1];
|
||
const doRob = dp[i-2] + nums[i];
|
||
steps.push({stage:'compare', msg:`dp[${i}]:不偷 = dp[${i-1}] = ${notRob},偷 = dp[${i-2}] + nums[${i}] = ${dp[i-2]} + ${nums[i]} = ${doRob}`, dp:[...dp], rob:[...rob], current:i, choice:'comparing', notRob, doRob});
|
||
if (doRob > notRob) {
|
||
dp[i] = doRob; rob[i] = true;
|
||
steps.push({stage:'rob', msg:`偷第 ${i} 间更优:dp[${i}] = ${doRob}`, dp:[...dp], rob:[...rob], current:i, choice:'rob'});
|
||
} else {
|
||
dp[i] = notRob; rob[i] = false;
|
||
steps.push({stage:'skip', msg:`不偷第 ${i} 间更优:dp[${i}] = ${notRob}`, dp:[...dp], rob:[...rob], current:i, choice:'skip'});
|
||
}
|
||
}
|
||
steps.push({stage:'done', msg:`最多可以偷取 ${dp[n-1]}`, dp:[...dp], rob:[...rob], current:n-1, choice:'done'});
|
||
}
|
||
|
||
function render(step) {
|
||
const s = steps[step];
|
||
let viz = '<div style="display:flex;gap:6px;margin:8px 0;">';
|
||
nums.forEach((v, i) => {
|
||
const isCurrent = i === s.current && s.stage !== 'done';
|
||
const isRobbed = s.rob[i];
|
||
let bg = '#e2e8f0', color = '#475569', border = '2px solid #cbd5e1';
|
||
if (isRobbed && s.stage === 'done') { bg = '#dcfce7'; color = '#166534'; border = '2px solid #16a34a'; }
|
||
else if (isCurrent && s.choice === 'rob') { bg = '#fef3c7'; color = '#92400e'; border = '2px solid #f59e0b'; }
|
||
else if (isCurrent) { bg = '#dbeafe'; color = '#1e40af'; border = '2px solid #3b82f6'; }
|
||
else if (isRobbed) { bg = '#dcfce7'; color = '#166534'; border = '2px solid #86efac'; }
|
||
viz += `<div style="width:56px;padding:8px 4px;background:${bg};border:${border};border-radius:10px;text-align:center;font-size:13px;font-weight:600;color:${color};transition:all 0.3s;">
|
||
<div style="font-size:16px;margin-bottom:2px;">🏠</div>
|
||
<div>${v}</div>
|
||
<div style="font-size:10px;color:${isRobbed?'#16a34a':'#94a3b8'};">${isRobbed?'偷':'·'}</div>
|
||
</div>`;
|
||
});
|
||
viz += '</div>';
|
||
viz += '<div style="margin-top:12px;"><b>dp 值:</b></div>';
|
||
const dpHl = {};
|
||
if (s.current >= 0) dpHl[s.current] = s.choice==='rob'?'orange':'blue';
|
||
viz += renderArray(s.dp, {highlights: dpHl});
|
||
if (s.stage === 'compare') {
|
||
viz += `<div class="formula-box" style="margin-top:8px;">dp[${s.current}] = max(不偷=${s.notRob}, 偷=${s.doRob})</div>`;
|
||
}
|
||
$('vizArea').innerHTML = viz;
|
||
|
||
$('detailContent').innerHTML = '<div class="calc-block">' + s.msg + '</div>';
|
||
if (s.dp.length > 0) {
|
||
$('detailContent').innerHTML += `<div class="current-answer">当前最大金额:<b>${s.dp[Math.max(0,s.current)]}</b></div>`;
|
||
}
|
||
if (s.stage === 'done') {
|
||
$('resultContent').innerHTML = `<div class="final-answer">最多可以偷取 <b>${s.dp[nums.length-1]}</b></div>`;
|
||
}
|
||
$('hintText').textContent = s.msg;
|
||
const stages = ['init→初始化','init2→初始化','compare→比较','rob→偷','skip→不偷','done→完成'];
|
||
$('pipeline').innerHTML = stages.map(st => {
|
||
const [k,l] = st.split('→');
|
||
return `<span class="pipe-step ${s.stage===k?'active':''}">${l}</span>`;
|
||
}).join('<i>→</i>');
|
||
}
|
||
|
||
function init() {
|
||
const sel = $('exampleSelect');
|
||
examples.forEach((e,i) => { sel.innerHTML += `<option value="${i}">${e.label}</option>`; });
|
||
$('inputArea').value = '[1,2,3,1]';
|
||
buildSteps(examples[0].input);
|
||
stepCtrl = new StepController({onStep: render});
|
||
stepCtrl.setSteps(steps.map((_,i)=>i));
|
||
$('stepInfo').textContent = '步骤 1 / ' + steps.length;
|
||
stepCtrl.onStep = (idx) => { render(idx); $('stepInfo').textContent = `步骤 ${idx+1} / ${steps.length}`; };
|
||
$('applyBtn').onclick = () => {
|
||
try { const arr = JSON.parse($('inputArea').value); buildSteps(arr); stepCtrl.setSteps(steps.map((_,i)=>i)); render(0); $('stepInfo').textContent = '步骤 1 / ' + steps.length; }
|
||
catch(e) { alert('请输入合法 JSON 数组'); }
|
||
};
|
||
$('exampleSelect').onchange = () => {
|
||
const e = examples[parseInt($('exampleSelect').value)];
|
||
$('inputArea').value = JSON.stringify(e.input);
|
||
buildSteps(e.input); stepCtrl.setSteps(steps.map((_,i)=>i)); render(0);
|
||
};
|
||
$('prevBtn').onclick = () => stepCtrl.prev();
|
||
$('nextBtn').onclick = () => stepCtrl.next();
|
||
$('jumpBtn').onclick = () => stepCtrl.jumpToEnd();
|
||
$('autoBtn').onclick = () => { const on = stepCtrl.toggleAuto(); $('autoBtn').textContent = on ? '暂停' : '自动播放'; };
|
||
$('resetBtn').onclick = () => { stepCtrl.reset(); $('autoBtn').textContent = '自动播放'; };
|
||
}
|
||
init();
|
||
$('codeArea').innerHTML = renderCode(`def rob(nums):
|
||
n = len(nums)
|
||
if n == 0: return 0
|
||
if n == 1: return nums[0]
|
||
dp = [0] * n
|
||
dp[0], dp[1] = nums[0], max(nums[0], nums[1])
|
||
for i in range(2, n):
|
||
dp[i] = max(dp[i-1], dp[i-2] + nums[i])
|
||
return dp[-1]`, {lang:'Python'});
|
||
'''
|
||
|
||
|
||
def js_perfect_squares():
|
||
return r'''
|
||
const examples = [
|
||
{input: 12, label: '示例1: n=12'},
|
||
{input: 13, label: '示例2: n=13'},
|
||
{input: 4, label: '示例3: n=4'},
|
||
];
|
||
let steps, stepCtrl, n;
|
||
|
||
function buildSteps(target) {
|
||
n = target; steps = [];
|
||
const dp = new Array(n + 1).fill(Infinity);
|
||
dp[0] = 0;
|
||
steps.push({stage:'init', msg:`初始化 dp[0]=0,其余为 ∞`, dp:[...dp], current:-1, bestJ:-1, candidates:[]});
|
||
for (let i = 1; i <= n; i++) {
|
||
let bestJ = 1, bestVal = Infinity;
|
||
const candidates = [];
|
||
for (let j = 1; j * j <= i; j++) {
|
||
candidates.push({j, sq: j*j, val: dp[i - j*j] + 1});
|
||
if (dp[i - j*j] + 1 < bestVal) { bestVal = dp[i - j*j] + 1; bestJ = j; }
|
||
}
|
||
steps.push({stage:'try', msg:`计算 dp[${i}]:尝试所有平方数`, dp:[...dp], current:i, bestJ:-1, candidates});
|
||
dp[i] = bestVal;
|
||
steps.push({stage:'fill', msg:`dp[${i}] = dp[${i-bestJ*bestJ}]+1 = ${dp[i-bestJ*bestJ]}+1 = ${bestVal}(选 ${bestJ}²=${bestJ*bestJ})`, dp:[...dp], current:i, bestJ, candidates});
|
||
}
|
||
steps.push({stage:'done', msg:`${n} 最少需要 ${dp[n]} 个完全平方数`, dp:[...dp], current:n, bestJ:-1, candidates:[]});
|
||
}
|
||
|
||
function render(step) {
|
||
const s = steps[step];
|
||
let viz = '<div style="margin-top:8px;"><b>dp 表:</b></div>';
|
||
viz += '<div style="display:flex;flex-wrap:wrap;gap:4px;margin:8px 0;">';
|
||
for (let i = 0; i <= n; i++) {
|
||
let cls = 'default';
|
||
if (i === s.current) cls = s.stage==='try'?'orange':'green';
|
||
if (s.bestJ > 0 && i === s.current - s.bestJ*s.bestJ) cls = 'blue';
|
||
const val = s.dp[i] === Infinity ? '∞' : s.dp[i];
|
||
viz += `<span class="chip-group"><span class="chip ${cls}" style="min-width:32px;font-size:13px;">${val}</span><span class="chip-index">${i}</span></span>`;
|
||
}
|
||
viz += '</div>';
|
||
if (s.candidates && s.candidates.length > 0) {
|
||
viz += '<div style="margin-top:8px;"><b>候选平方数:</b></div>';
|
||
viz += '<div style="display:flex;gap:6px;flex-wrap:wrap;margin:4px 0;">';
|
||
s.candidates.forEach(c => {
|
||
const isBest = c.j === s.bestJ;
|
||
const bg = isBest ? '#dcfce7' : '#f0f9ff';
|
||
const bd = isBest ? '2px solid #16a34a' : '1px solid #cbd5e1';
|
||
viz += `<span style="padding:4px 10px;background:${bg};border:${bd};border-radius:8px;font-size:13px;">${c.j}²=${c.sq} → dp[${s.current-c.sq}]+1=${c.val===Infinity?'∞':c.val}</span>`;
|
||
});
|
||
viz += '</div>';
|
||
}
|
||
$('vizArea').innerHTML = viz;
|
||
|
||
$('detailContent').innerHTML = '<div class="calc-block">' + s.msg + '</div>';
|
||
if (s.current > 0 && s.dp[s.current] !== Infinity) {
|
||
$('detailContent').innerHTML += `<div class="current-answer">dp[${s.current}] = <b>${s.dp[s.current]}</b></div>`;
|
||
}
|
||
if (s.stage === 'done') {
|
||
$('resultContent').innerHTML = `<div class="final-answer">${n} 最少需要 <b>${s.dp[n]}</b> 个完全平方数</div>`;
|
||
}
|
||
$('hintText').textContent = s.msg;
|
||
const stages = ['init→初始化','try→尝试','fill→填充','done→完成'];
|
||
$('pipeline').innerHTML = stages.map(st => {
|
||
const [k,l] = st.split('→');
|
||
return `<span class="pipe-step ${s.stage===k?'active':''}">${l}</span>`;
|
||
}).join('<i>→</i>');
|
||
}
|
||
|
||
function init() {
|
||
const sel = $('exampleSelect');
|
||
examples.forEach((e,i) => { sel.innerHTML += `<option value="${i}">${e.label}</option>`; });
|
||
$('inputArea').value = 'n=12';
|
||
buildSteps(examples[0].input);
|
||
stepCtrl = new StepController({onStep: render});
|
||
stepCtrl.setSteps(steps.map((_,i)=>i));
|
||
$('stepInfo').textContent = '步骤 1 / ' + steps.length;
|
||
stepCtrl.onStep = (idx) => { render(idx); $('stepInfo').textContent = `步骤 ${idx+1} / ${steps.length}`; };
|
||
$('applyBtn').onclick = () => {
|
||
const m = $('inputArea').value.match(/n=(\d+)/);
|
||
if (!m) { alert('格式: n=12'); return; }
|
||
buildSteps(parseInt(m[1]));
|
||
stepCtrl.setSteps(steps.map((_,i)=>i)); render(0); $('stepInfo').textContent = '步骤 1 / ' + steps.length;
|
||
};
|
||
$('exampleSelect').onchange = () => {
|
||
const e = examples[parseInt($('exampleSelect').value)];
|
||
$('inputArea').value = `n=${e.input}`;
|
||
buildSteps(e.input); stepCtrl.setSteps(steps.map((_,i)=>i)); render(0);
|
||
};
|
||
$('prevBtn').onclick = () => stepCtrl.prev();
|
||
$('nextBtn').onclick = () => stepCtrl.next();
|
||
$('jumpBtn').onclick = () => stepCtrl.jumpToEnd();
|
||
$('autoBtn').onclick = () => { const on = stepCtrl.toggleAuto(); $('autoBtn').textContent = on ? '暂停' : '自动播放'; };
|
||
$('resetBtn').onclick = () => { stepCtrl.reset(); $('autoBtn').textContent = '自动播放'; };
|
||
}
|
||
init();
|
||
$('codeArea').innerHTML = renderCode(`def numSquares(n):
|
||
dp = [float('inf')] * (n + 1)
|
||
dp[0] = 0
|
||
for i in range(1, n + 1):
|
||
j = 1
|
||
while j * j <= i:
|
||
dp[i] = min(dp[i], dp[i - j*j] + 1)
|
||
j += 1
|
||
return dp[n]`, {lang:'Python'});
|
||
'''
|
||
|
||
|
||
def js_coin_change():
|
||
return r'''
|
||
const examples = [
|
||
{input: [1,2,5], amount: 11, label: '示例1: coins=[1,2,5], amount=11'},
|
||
{input: [2], amount: 3, label: '示例2: coins=[2], amount=3'},
|
||
{input: [1], amount: 0, label: '示例3: coins=[1], amount=0'},
|
||
];
|
||
let coins, amount, steps, stepCtrl;
|
||
|
||
function buildSteps(c, amt) {
|
||
coins = [...c]; amount = amt; steps = [];
|
||
const dp = new Array(amt + 1).fill(Infinity);
|
||
dp[0] = 0;
|
||
steps.push({stage:'init', msg:'初始化 dp[0]=0,其余为 ∞', dp:[...dp], current:-1, bestCoin:-1, candidates:[]});
|
||
for (let i = 1; i <= amt; i++) {
|
||
let bestCoin = -1, bestVal = Infinity;
|
||
const candidates = [];
|
||
for (const coin of coins) {
|
||
if (coin <= i) {
|
||
const val = dp[i - coin] + 1;
|
||
candidates.push({coin, prev: i - coin, val});
|
||
if (val < bestVal) { bestVal = val; bestCoin = coin; }
|
||
}
|
||
}
|
||
steps.push({stage:'try', msg:`计算 dp[${i}]:尝试所有硬币`, dp:[...dp], current:i, bestCoin:-1, candidates});
|
||
dp[i] = bestVal;
|
||
if (bestCoin > 0) {
|
||
steps.push({stage:'fill', msg:`dp[${i}] = dp[${i-bestCoin}]+1 = ${dp[i-bestCoin]}+1 = ${bestVal}(选硬币 ${bestCoin})`, dp:[...dp], current:i, bestCoin, candidates});
|
||
} else {
|
||
steps.push({stage:'fill', msg:`dp[${i}] = ∞(无法凑出)`, dp:[...dp], current:i, bestCoin:-1, candidates});
|
||
}
|
||
}
|
||
steps.push({stage:'done', msg:dp[amt]===Infinity?`无法凑出金额 ${amt}`:`凑出金额 ${amt} 最少需要 ${dp[amt]} 枚硬币`, dp:[...dp], current:amt, bestCoin:-1, candidates:[]});
|
||
}
|
||
|
||
function render(step) {
|
||
const s = steps[step];
|
||
let viz = '<div style="margin-top:8px;"><b>金额轴 & dp 表:</b></div>';
|
||
viz += '<div style="display:flex;flex-wrap:wrap;gap:4px;margin:8px 0;">';
|
||
for (let i = 0; i <= amount; i++) {
|
||
const cls = i === s.current ? (s.stage==='try'?'orange':'green') : 'default';
|
||
const val = s.dp[i] === Infinity ? '∞' : s.dp[i];
|
||
viz += `<span class="chip-group"><span class="chip ${cls}" style="min-width:36px;font-size:13px;">${val}</span><span class="chip-index">${i}</span></span>`;
|
||
}
|
||
viz += '</div>';
|
||
viz += '<div style="margin-top:8px;"><b>硬币面额:</b></div>';
|
||
viz += '<div style="display:flex;gap:6px;margin:4px 0;">';
|
||
coins.forEach(c => {
|
||
const isBest = c === s.bestCoin;
|
||
const cls = isBest ? 'green' : 'default';
|
||
viz += `<span class="chip ${cls}" style="border-radius:50%;min-width:36px;height:36px;">${c}</span>`;
|
||
});
|
||
viz += '</div>';
|
||
if (s.candidates && s.candidates.length > 0) {
|
||
viz += '<div style="margin-top:8px;"><b>候选:</b></div>';
|
||
viz += '<div style="display:flex;gap:6px;flex-wrap:wrap;margin:4px 0;">';
|
||
s.candidates.forEach(c => {
|
||
const isBest = c.coin === s.bestCoin;
|
||
const bg = isBest ? '#dcfce7' : '#f0f9ff';
|
||
const bd = isBest ? '2px solid #16a34a' : '1px solid #cbd5e1';
|
||
viz += `<span style="padding:4px 10px;background:${bg};border:${bd};border-radius:8px;font-size:13px;">用${c.coin} → dp[${c.prev}]+1=${c.val===Infinity?'∞':c.val}</span>`;
|
||
});
|
||
viz += '</div>';
|
||
}
|
||
$('vizArea').innerHTML = viz;
|
||
|
||
$('detailContent').innerHTML = '<div class="calc-block">' + s.msg + '</div>';
|
||
if (s.current >= 0 && s.dp[s.current] !== Infinity) {
|
||
$('detailContent').innerHTML += `<div class="current-answer">dp[${s.current}] = <b>${s.dp[s.current]}</b></div>`;
|
||
}
|
||
if (s.stage === 'done') {
|
||
if (s.dp[amount] === Infinity) {
|
||
$('resultContent').innerHTML = '<div class="final-answer" style="border-color:#f87171;background:#fef2f2;">无法凑出该金额,返回 <b>-1</b></div>';
|
||
} else {
|
||
$('resultContent').innerHTML = `<div class="final-answer">最少需要 <b>${s.dp[amount]}</b> 枚硬币</div>`;
|
||
}
|
||
}
|
||
$('hintText').textContent = s.msg;
|
||
const stages = ['init→初始化','try→尝试','fill→填充','done→完成'];
|
||
$('pipeline').innerHTML = stages.map(st => {
|
||
const [k,l] = st.split('→');
|
||
return `<span class="pipe-step ${s.stage===k?'active':''}">${l}</span>`;
|
||
}).join('<i>→</i>');
|
||
}
|
||
|
||
function init() {
|
||
const sel = $('exampleSelect');
|
||
examples.forEach((e,i) => { sel.innerHTML += `<option value="${i}">${e.label}</option>`; });
|
||
$('inputArea').value = 'coins=[1,2,5], amount=11';
|
||
buildSteps(examples[0].input, examples[0].amount);
|
||
stepCtrl = new StepController({onStep: render});
|
||
stepCtrl.setSteps(steps.map((_,i)=>i));
|
||
$('stepInfo').textContent = '步骤 1 / ' + steps.length;
|
||
stepCtrl.onStep = (idx) => { render(idx); $('stepInfo').textContent = `步骤 ${idx+1} / ${steps.length}`; };
|
||
$('applyBtn').onclick = () => {
|
||
const m = $('inputArea').value.match(/coins=\[([^\]]+)\].*amount=(\d+)/);
|
||
if (!m) { alert('格式: coins=[1,2,5], amount=11'); return; }
|
||
const c = m[1].split(',').map(Number);
|
||
buildSteps(c, parseInt(m[2]));
|
||
stepCtrl.setSteps(steps.map((_,i)=>i)); render(0); $('stepInfo').textContent = '步骤 1 / ' + steps.length;
|
||
};
|
||
$('exampleSelect').onchange = () => {
|
||
const e = examples[parseInt($('exampleSelect').value)];
|
||
$('inputArea').value = `coins=[${e.input}], amount=${e.amount}`;
|
||
buildSteps(e.input, e.amount); stepCtrl.setSteps(steps.map((_,i)=>i)); render(0);
|
||
};
|
||
$('prevBtn').onclick = () => stepCtrl.prev();
|
||
$('nextBtn').onclick = () => stepCtrl.next();
|
||
$('jumpBtn').onclick = () => stepCtrl.jumpToEnd();
|
||
$('autoBtn').onclick = () => { const on = stepCtrl.toggleAuto(); $('autoBtn').textContent = on ? '暂停' : '自动播放'; };
|
||
$('resetBtn').onclick = () => { stepCtrl.reset(); $('autoBtn').textContent = '自动播放'; };
|
||
}
|
||
init();
|
||
$("codeArea").innerHTML = renderCode(
|
||
`def coinChange(coins, amount):\n` +
|
||
` dp = [float('inf')] * (amount + 1)\n` +
|
||
` dp[0] = 0\n` +
|
||
` for i in range(1, amount + 1):\n` +
|
||
` for coin in coins:\n` +
|
||
` if coin <= i:\n` +
|
||
` dp[i] = min(dp[i], dp[i - coin] + 1)\n` +
|
||
` return dp[amount] if dp[amount] != float('inf') else -1`,
|
||
{lang:'Python'}
|
||
);
|
||
'''
|
||
|
||
|
||
def js_word_break():
|
||
return r'''
|
||
const examples = [
|
||
{input: 'leetcode', dict: ['leet','code'], label: '示例1: "leetcode", ["leet","code"]'},
|
||
{input: 'applepenapple', dict: ['apple','pen'], label: '示例2: "applepenapple", ["apple","pen"]'},
|
||
{input: 'catsandog', dict: ['cats','dog','sand','and','cat'], label: '示例3: "catsandog"'},
|
||
];
|
||
let s, wordDict, steps, stepCtrl;
|
||
|
||
function buildSteps(str, dict) {
|
||
s = str; wordDict = dict; steps = [];
|
||
const n = str.length;
|
||
const dp = new Array(n + 1).fill(false);
|
||
dp[0] = true;
|
||
steps.push({stage:'init', msg:'dp[0]=true(空串可分割)', dp:[...dp], i:-1, j:-1, matched:false, matchWord:''});
|
||
for (let i = 1; i <= n; i++) {
|
||
steps.push({stage:'checkI', msg:`检查 dp[${i}]:能否将 s[0:${i}]="${s.slice(0,i)}" 分割`, dp:[...dp], i, j:-1, matched:false, matchWord:''});
|
||
for (let j = 0; j < i; j++) {
|
||
const sub = s.slice(j, i);
|
||
const inDict = dp[j] && dict.includes(sub);
|
||
if (dp[j]) {
|
||
steps.push({stage:'tryJ', msg:`dp[${j}]=true,检查 s[${j}:${i}]="${sub}" ${inDict?'在字典中 ✅':'不在字典中 ❌'}`, dp:[...dp], i, j, matched:inDict, matchWord:sub});
|
||
if (inDict) {
|
||
dp[i] = true;
|
||
steps.push({stage:'found', msg:`dp[${j}]=true 且 "${sub}" 在字典中 → dp[${i}]=true`, dp:[...dp], i, j, matched:true, matchWord:sub});
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
if (!dp[i]) {
|
||
steps.push({stage:'fail', msg:`dp[${i}]=false(无法分割 s[0:${i}])`, dp:[...dp], i, j:-1, matched:false, matchWord:''});
|
||
}
|
||
}
|
||
steps.push({stage:'done', msg:dp[n]?`"${s}" 可以被分割`:`"${s}" 无法被分割`, dp:[...dp], i:n, j:-1, matched:dp[n], matchWord:''});
|
||
}
|
||
|
||
function render(step) {
|
||
const st = steps[step];
|
||
let viz = '<div style="margin-top:8px;"><b>字符串:</b></div>';
|
||
viz += '<div style="display:flex;gap:2px;margin:8px 0;">';
|
||
for (let i = 0; i < s.length; i++) {
|
||
const inRange = st.j >= 0 && i >= st.j && i < st.i;
|
||
const cls = inRange ? (st.matched ? 'green' : 'orange') : 'default';
|
||
viz += `<span class="chip ${cls}" style="min-width:28px;font-size:14px;">${s[i]}</span>`;
|
||
}
|
||
viz += '</div>';
|
||
viz += '<div style="display:flex;gap:2px;margin-top:-4px;">';
|
||
for (let i = 0; i <= s.length; i++) {
|
||
viz += `<span style="min-width:28px;text-align:center;font-size:10px;color:var(--text-muted);">${i}</span>`;
|
||
}
|
||
viz += '</div>';
|
||
viz += '<div style="margin-top:8px;"><b>dp 状态:</b></div>';
|
||
viz += '<div style="display:flex;gap:3px;flex-wrap:wrap;margin:4px 0;">';
|
||
for (let i = 0; i <= s.length; i++) {
|
||
const isCur = i === st.i;
|
||
const cls = isCur ? 'orange' : (st.dp[i] ? 'green' : 'default');
|
||
viz += `<span class="chip ${cls}" style="min-width:32px;font-size:12px;height:28px;">${st.dp[i]?'T':'F'}</span>`;
|
||
}
|
||
viz += '</div>';
|
||
viz += '<div style="margin-top:8px;"><b>字典:</b></div>';
|
||
viz += '<div style="display:flex;gap:4px;flex-wrap:wrap;margin:4px 0;">';
|
||
wordDict.forEach(w => {
|
||
const isMatch = w === st.matchWord;
|
||
const cls = isMatch ? 'green' : 'default';
|
||
viz += `<span class="chip ${cls}" style="min-width:auto;padding:4px 10px;font-size:13px;">"${w}"</span>`;
|
||
});
|
||
viz += '</div>';
|
||
$('vizArea').innerHTML = viz;
|
||
|
||
$('detailContent').innerHTML = '<div class="calc-block">' + st.msg + '</div>';
|
||
if (st.stage === 'done') {
|
||
$('resultContent').innerHTML = `<div class="final-answer">${st.matched ? `"${s}" 可以被分割 ✅` : `"${s}" 无法被分割 ❌`}</div>`;
|
||
}
|
||
$('hintText').textContent = st.msg;
|
||
const stages = ['init→初始化','checkI→检查','tryJ→尝试','found→找到','fail→失败','done→完成'];
|
||
$('pipeline').innerHTML = stages.map(st2 => {
|
||
const [k,l] = st2.split('→');
|
||
return `<span class="pipe-step ${st.stage===k?'active':''}">${l}</span>`;
|
||
}).join('<i>→</i>');
|
||
}
|
||
|
||
function init() {
|
||
const sel = $('exampleSelect');
|
||
examples.forEach((e,i) => { sel.innerHTML += `<option value="${i}">${e.label}</option>`; });
|
||
$('inputArea').value = 's="leetcode", dict=["leet","code"]';
|
||
buildSteps(examples[0].input, examples[0].dict);
|
||
stepCtrl = new StepController({onStep: render});
|
||
stepCtrl.setSteps(steps.map((_,i)=>i));
|
||
$('stepInfo').textContent = '步骤 1 / ' + steps.length;
|
||
stepCtrl.onStep = (idx) => { render(idx); $('stepInfo').textContent = `步骤 ${idx+1} / ${steps.length}`; };
|
||
$('applyBtn').onclick = () => {
|
||
const m = $('inputArea').value.match(/s="([^"]+)"\s*,\s*dict=\[([^\]]+)\]/);
|
||
if (!m) { alert('格式: s="leetcode", dict=["leet","code"]'); return; }
|
||
const dict = m[2].match(/"([^"]*)"/g).map(x => x.replace(/"/g, ''));
|
||
buildSteps(m[1], dict);
|
||
stepCtrl.setSteps(steps.map((_,i)=>i)); render(0); $('stepInfo').textContent = '步骤 1 / ' + steps.length;
|
||
};
|
||
$('exampleSelect').onchange = () => {
|
||
const e = examples[parseInt($('exampleSelect').value)];
|
||
$('inputArea').value = `s="${e.input}", dict=["${e.dict.join('","')}"]`;
|
||
buildSteps(e.input, e.dict); stepCtrl.setSteps(steps.map((_,i)=>i)); render(0);
|
||
};
|
||
$('prevBtn').onclick = () => stepCtrl.prev();
|
||
$('nextBtn').onclick = () => stepCtrl.next();
|
||
$('jumpBtn').onclick = () => stepCtrl.jumpToEnd();
|
||
$('autoBtn').onclick = () => { const on = stepCtrl.toggleAuto(); $('autoBtn').textContent = on ? '暂停' : '自动播放'; };
|
||
$('resetBtn').onclick = () => { stepCtrl.reset(); $('autoBtn').textContent = '自动播放'; };
|
||
}
|
||
init();
|
||
$('codeArea').innerHTML = renderCode(`def wordBreak(s, wordDict):
|
||
n = len(s)
|
||
dp = [False] * (n + 1)
|
||
dp[0] = True
|
||
word_set = set(wordDict)
|
||
for i in range(1, n + 1):
|
||
for j in range(i):
|
||
if dp[j] and s[j:i] in word_set:
|
||
dp[i] = True
|
||
break
|
||
return dp[n]`, {lang:'Python'});
|
||
'''
|
||
|
||
|
||
def js_longest_increasing_subsequence():
|
||
return r'''
|
||
const examples = [
|
||
{input: [10,9,2,5,3,7,101,18], label: '示例1: [10,9,2,5,3,7,101,18]'},
|
||
{input: [0,1,0,3,2,3], label: '示例2: [0,1,0,3,2,3]'},
|
||
{input: [7,7,7,7,7,7,7], label: '示例3: [7,7,7,7,7,7,7]'},
|
||
];
|
||
let nums, steps, stepCtrl;
|
||
|
||
function buildSteps(arr) {
|
||
nums = [...arr]; steps = [];
|
||
const n = nums.length;
|
||
if (n === 0) { steps.push({stage:'done', msg:'数组为空', dp:[], best:0, prev:[]}); return; }
|
||
const dp = new Array(n).fill(1);
|
||
const prev = new Array(n).fill(-1);
|
||
steps.push({stage:'init', msg:'每个元素自身构成长度为1的递增子序列', dp:[...dp], current:-1, bestIdx:0, prev:[...prev]});
|
||
let bestIdx = 0;
|
||
for (let i = 1; i < n; i++) {
|
||
steps.push({stage:'check', msg:`检查 nums[${i}]=${nums[i]},寻找 j<i 使得 nums[j]<nums[${i}]`, dp:[...dp], current:i, bestIdx, j:-1, prev:[...prev]});
|
||
for (let j = 0; j < i; j++) {
|
||
if (nums[j] < nums[i] && dp[j] + 1 > dp[i]) {
|
||
dp[i] = dp[j] + 1;
|
||
prev[i] = j;
|
||
steps.push({stage:'update', msg:`nums[${j}]=${nums[j]} < ${nums[i]},dp[${i}] = dp[${j}]+1 = ${dp[i]}`, dp:[...dp], current:i, bestIdx, j, prev:[...prev]});
|
||
}
|
||
}
|
||
if (dp[i] > dp[bestIdx]) bestIdx = i;
|
||
steps.push({stage:'doneI', msg:`dp[${i}]=${dp[i]},最长子序列长度 = ${dp[bestIdx]}`, dp:[...dp], current:i, bestIdx, prev:[...prev]});
|
||
}
|
||
steps.push({stage:'done', msg:`最长递增子序列长度 = ${dp[bestIdx]}`, dp:[...dp], current:bestIdx, bestIdx, prev:[...prev]});
|
||
}
|
||
|
||
function render(step) {
|
||
const s = steps[step];
|
||
const hl = {};
|
||
if (s.current >= 0 && s.stage !== 'done') hl[s.current] = 'orange';
|
||
if (s.j >= 0) hl[s.j] = 'blue';
|
||
if (s.stage === 'done') {
|
||
let idx = s.bestIdx;
|
||
while (idx >= 0) { hl[idx] = 'green'; idx = s.prev[idx]; }
|
||
}
|
||
let viz = renderArray(nums, {highlights: hl});
|
||
viz += '<div style="margin-top:8px;"><b>dp 值(以每个元素结尾的 LIS 长度):</b></div>';
|
||
const dpHl = {};
|
||
if (s.current >= 0 && s.stage !== 'done') dpHl[s.current] = 'orange';
|
||
if (s.bestIdx >= 0 && s.stage === 'done') dpHl[s.bestIdx] = 'green';
|
||
viz += renderArray(s.dp, {highlights: dpHl});
|
||
if (s.stage === 'done') {
|
||
const lis = [];
|
||
let idx = s.bestIdx;
|
||
while (idx >= 0) { lis.unshift(nums[idx]); idx = s.prev[idx]; }
|
||
viz += `<div style="margin-top:8px;color:var(--green);"><b>LIS: [${lis.join(', ')}]</b></div>`;
|
||
}
|
||
$('vizArea').innerHTML = viz;
|
||
|
||
$('detailContent').innerHTML = '<div class="calc-block">' + s.msg + '</div>';
|
||
if (s.bestIdx >= 0) {
|
||
$('detailContent').innerHTML += `<div class="current-answer">当前 LIS 长度:<b>${s.dp[s.bestIdx]||1}</b></div>`;
|
||
}
|
||
if (s.stage === 'done') {
|
||
$('resultContent').innerHTML = `<div class="final-answer">最长递增子序列长度 = <b>${s.dp[s.bestIdx]}</b></div>`;
|
||
}
|
||
$('hintText').textContent = s.msg;
|
||
const stages = ['init→初始化','check→检查','update→更新','doneI→完成I','done→完成'];
|
||
$('pipeline').innerHTML = stages.map(st => {
|
||
const [k,l] = st.split('→');
|
||
return `<span class="pipe-step ${s.stage===k?'active':''}">${l}</span>`;
|
||
}).join('<i>→</i>');
|
||
}
|
||
|
||
function init() {
|
||
const sel = $('exampleSelect');
|
||
examples.forEach((e,i) => { sel.innerHTML += `<option value="${i}">${e.label}</option>`; });
|
||
$('inputArea').value = '[10,9,2,5,3,7,101,18]';
|
||
buildSteps(examples[0].input);
|
||
stepCtrl = new StepController({onStep: render});
|
||
stepCtrl.setSteps(steps.map((_,i)=>i));
|
||
$('stepInfo').textContent = '步骤 1 / ' + steps.length;
|
||
stepCtrl.onStep = (idx) => { render(idx); $('stepInfo').textContent = `步骤 ${idx+1} / ${steps.length}`; };
|
||
$('applyBtn').onclick = () => {
|
||
try { const arr = JSON.parse($('inputArea').value); buildSteps(arr); stepCtrl.setSteps(steps.map((_,i)=>i)); render(0); $('stepInfo').textContent = '步骤 1 / ' + steps.length; }
|
||
catch(e) { alert('请输入合法 JSON 数组'); }
|
||
};
|
||
$('exampleSelect').onchange = () => {
|
||
const e = examples[parseInt($('exampleSelect').value)];
|
||
$('inputArea').value = JSON.stringify(e.input);
|
||
buildSteps(e.input); stepCtrl.setSteps(steps.map((_,i)=>i)); render(0);
|
||
};
|
||
$('prevBtn').onclick = () => stepCtrl.prev();
|
||
$('nextBtn').onclick = () => stepCtrl.next();
|
||
$('jumpBtn').onclick = () => stepCtrl.jumpToEnd();
|
||
$('autoBtn').onclick = () => { const on = stepCtrl.toggleAuto(); $('autoBtn').textContent = on ? '暂停' : '自动播放'; };
|
||
$('resetBtn').onclick = () => { stepCtrl.reset(); $('autoBtn').textContent = '自动播放'; };
|
||
}
|
||
init();
|
||
$('codeArea').innerHTML = renderCode(`def lengthOfLIS(nums):
|
||
n = len(nums)
|
||
dp = [1] * n
|
||
for i in range(1, n):
|
||
for j in range(i):
|
||
if nums[j] < nums[i]:
|
||
dp[i] = max(dp[i], dp[j] + 1)
|
||
return max(dp)`, {lang:'Python'});
|
||
'''
|
||
|
||
|
||
def js_maximum_product_subarray():
|
||
return r'''
|
||
const examples = [
|
||
{input: [2,3,-2,4], label: '示例1: [2,3,-2,4]'},
|
||
{input: [-2,0,-1], label: '示例2: [-2,0,-1]'},
|
||
{input: [-2,3,-4], label: '示例3: [-2,3,-4]'},
|
||
];
|
||
let nums, steps, stepCtrl;
|
||
|
||
function buildSteps(arr) {
|
||
nums = [...arr]; steps = [];
|
||
const n = nums.length;
|
||
const maxDP = new Array(n).fill(0);
|
||
const minDP = new Array(n).fill(0);
|
||
maxDP[0] = nums[0]; minDP[0] = nums[0];
|
||
let globalMax = nums[0];
|
||
steps.push({stage:'init', msg:`maxDP[0]=${nums[0]},minDP[0]=${nums[0]}(同时维护最大和最小)`, maxDP:[...maxDP], minDP:[...minDP], current:-1, globalMax, candidates:null});
|
||
for (let i = 1; i < n; i++) {
|
||
const candidates = [nums[i], maxDP[i-1]*nums[i], minDP[i-1]*nums[i]];
|
||
maxDP[i] = Math.max(...candidates);
|
||
minDP[i] = Math.min(...candidates);
|
||
globalMax = Math.max(globalMax, maxDP[i]);
|
||
steps.push({stage:'calc', msg:`nums[${i}]=${nums[i]}:候选值 = [${nums[i]}, ${maxDP[i-1]}×${nums[i]}=${candidates[1]}, ${minDP[i-1]}×${nums[i]}=${candidates[2]}]`, maxDP:[...maxDP], minDP:[...minDP], current:i, globalMax, candidates});
|
||
steps.push({stage:'fill', msg:`maxDP[${i}]=${maxDP[i]},minDP[${i}]=${minDP[i]},全局最大=${globalMax}`, maxDP:[...maxDP], minDP:[...minDP], current:i, globalMax, candidates});
|
||
}
|
||
steps.push({stage:'done', msg:`乘积最大子数组的积 = ${globalMax}`, maxDP:[...maxDP], minDP:[...minDP], current:n-1, globalMax, candidates:null});
|
||
}
|
||
|
||
function render(step) {
|
||
const s = steps[step];
|
||
const hl = {};
|
||
if (s.current >= 0) hl[s.current] = 'orange';
|
||
let viz = renderArray(nums, {highlights: hl});
|
||
viz += '<div style="margin-top:12px;"><b>maxDP(最大乘积):</b></div>';
|
||
const maxHl = {};
|
||
if (s.current >= 0) maxHl[s.current] = 'green';
|
||
viz += renderArray(s.maxDP, {highlights: maxHl});
|
||
viz += '<div style="margin-top:8px;"><b>minDP(最小乘积):</b></div>';
|
||
const minHl = {};
|
||
if (s.current >= 0) minHl[s.current] = 'purple';
|
||
viz += renderArray(s.minDP, {highlights: minHl});
|
||
if (s.candidates) {
|
||
viz += '<div style="margin-top:8px;"><b>三个候选:</b></div>';
|
||
viz += '<div style="display:flex;gap:8px;margin:4px 0;">';
|
||
const labels = ['当前数', 'maxDP×当前', 'minDP×当前'];
|
||
s.candidates.forEach((c, idx) => {
|
||
const isMax = c === s.maxDP[s.current];
|
||
const isMin = c === s.minDP[s.current];
|
||
let bd = '1px solid #cbd5e1', bg = '#f8fafc';
|
||
if (isMax && isMin) { bd = '2px solid #8b5cf6'; bg = '#ede9fe'; }
|
||
else if (isMax) { bd = '2px solid #16a34a'; bg = '#dcfce7'; }
|
||
else if (isMin) { bd = '2px solid #7c3aed'; bg = '#ede9fe'; }
|
||
viz += `<span style="padding:4px 10px;background:${bg};border:${bd};border-radius:8px;font-size:13px;">${labels[idx]}=<b>${c}</b></span>`;
|
||
});
|
||
viz += '</div>';
|
||
}
|
||
$('vizArea').innerHTML = viz;
|
||
|
||
$('detailContent').innerHTML = '<div class="calc-block">' + s.msg + '</div>';
|
||
$('detailContent').innerHTML += `<div class="current-answer">全局最大乘积:<b>${s.globalMax}</b></div>`;
|
||
if (s.stage === 'done') {
|
||
$('resultContent').innerHTML = `<div class="final-answer">乘积最大子数组的积 = <b>${s.globalMax}</b></div>`;
|
||
}
|
||
$('hintText').textContent = s.msg;
|
||
const stages = ['init→初始化','calc→计算','fill→填充','done→完成'];
|
||
$('pipeline').innerHTML = stages.map(st => {
|
||
const [k,l] = st.split('→');
|
||
return `<span class="pipe-step ${s.stage===k?'active':''}">${l}</span>`;
|
||
}).join('<i>→</i>');
|
||
}
|
||
|
||
function init() {
|
||
const sel = $('exampleSelect');
|
||
examples.forEach((e,i) => { sel.innerHTML += `<option value="${i}">${e.label}</option>`; });
|
||
$('inputArea').value = '[2,3,-2,4]';
|
||
buildSteps(examples[0].input);
|
||
stepCtrl = new StepController({onStep: render});
|
||
stepCtrl.setSteps(steps.map((_,i)=>i));
|
||
$('stepInfo').textContent = '步骤 1 / ' + steps.length;
|
||
stepCtrl.onStep = (idx) => { render(idx); $('stepInfo').textContent = `步骤 ${idx+1} / ${steps.length}`; };
|
||
$('applyBtn').onclick = () => {
|
||
try { const arr = JSON.parse($('inputArea').value); buildSteps(arr); stepCtrl.setSteps(steps.map((_,i)=>i)); render(0); $('stepInfo').textContent = '步骤 1 / ' + steps.length; }
|
||
catch(e) { alert('请输入合法 JSON 数组'); }
|
||
};
|
||
$('exampleSelect').onchange = () => {
|
||
const e = examples[parseInt($('exampleSelect').value)];
|
||
$('inputArea').value = JSON.stringify(e.input);
|
||
buildSteps(e.input); stepCtrl.setSteps(steps.map((_,i)=>i)); render(0);
|
||
};
|
||
$('prevBtn').onclick = () => stepCtrl.prev();
|
||
$('nextBtn').onclick = () => stepCtrl.next();
|
||
$('jumpBtn').onclick = () => stepCtrl.jumpToEnd();
|
||
$('autoBtn').onclick = () => { const on = stepCtrl.toggleAuto(); $('autoBtn').textContent = on ? '暂停' : '自动播放'; };
|
||
$('resetBtn').onclick = () => { stepCtrl.reset(); $('autoBtn').textContent = '自动播放'; };
|
||
}
|
||
init();
|
||
$('codeArea').innerHTML = renderCode(`def maxProduct(nums):
|
||
max_dp = min_dp = result = nums[0]
|
||
for i in range(1, len(nums)):
|
||
candidates = (nums[i], max_dp * nums[i], min_dp * nums[i])
|
||
max_dp = max(candidates)
|
||
min_dp = min(candidates)
|
||
result = max(result, max_dp)
|
||
return result`, {lang:'Python'});
|
||
'''
|
||
|
||
|
||
def js_partition_equal_subset_sum():
|
||
return r'''
|
||
const examples = [
|
||
{input: [1,5,11,5], label: '示例1: [1,5,11,5]'},
|
||
{input: [1,2,3,5], label: '示例2: [1,2,3,5]'},
|
||
{input: [1,1], label: '示例3: [1,1]'},
|
||
];
|
||
let nums, steps, stepCtrl, target;
|
||
|
||
function buildSteps(arr) {
|
||
nums = [...arr]; steps = [];
|
||
const sum = arr.reduce((a,b) => a + b, 0);
|
||
if (sum % 2 !== 0) {
|
||
target = 0;
|
||
steps.push({stage:'done', msg:`总和=${sum}为奇数,无法等分`, dp:null, target:0, itemIdx:-1, curJ:-1});
|
||
return;
|
||
}
|
||
target = sum / 2;
|
||
const n = arr.length;
|
||
const dp = new Array(target + 1).fill(false);
|
||
dp[0] = true;
|
||
steps.push({stage:'init', msg:`总和=${sum},目标=${target},0-1背包:每件物品选或不选`, dp:[...dp], target, itemIdx:-1, curJ:-1});
|
||
for (let i = 0; i < n; i++) {
|
||
steps.push({stage:'item', msg:`考虑物品 ${i}:nums[${i}]=${arr[i]}`, dp:[...dp], target, itemIdx:i, curJ:-1});
|
||
for (let j = target; j >= arr[i]; j--) {
|
||
if (dp[j - arr[i]] && !dp[j]) {
|
||
dp[j] = true;
|
||
steps.push({stage:'fill', msg:`dp[${j}] = dp[${j}-${arr[i]}] = dp[${j-arr[i]}] = true → dp[${j}]=true`, dp:[...dp], target, itemIdx:i, curJ:j});
|
||
}
|
||
}
|
||
if (dp[target]) {
|
||
steps.push({stage:'done', msg:`已找到和为 ${target} 的子集,可以等分!`, dp:[...dp], target, itemIdx:i, curJ:target});
|
||
return;
|
||
}
|
||
}
|
||
steps.push({stage:'done', msg:`无法找到和为 ${target} 的子集`, dp:[...dp], target, itemIdx:-1, curJ:-1});
|
||
}
|
||
|
||
function render(step) {
|
||
const s = steps[step];
|
||
if (!s.dp) {
|
||
$('vizArea').innerHTML = '<div class="formula-box">总和为奇数,直接返回 false</div>';
|
||
$('detailContent').innerHTML = '<div class="calc-block">' + s.msg + '</div>';
|
||
$('resultContent').innerHTML = '<div class="final-answer" style="border-color:#f87171;background:#fef2f2;">无法等分</div>';
|
||
$('hintText').textContent = s.msg;
|
||
$('pipeline').innerHTML = '<span class="pipe-step active">完成</span>';
|
||
return;
|
||
}
|
||
let viz = '<div style="margin-top:8px;"><b>物品:</b></div>';
|
||
viz += '<div style="display:flex;gap:4px;margin:4px 0;">';
|
||
nums.forEach((v, i) => {
|
||
const cls = i === s.itemIdx ? 'orange' : (i < s.itemIdx || s.stage==='done' ? 'green' : 'default');
|
||
viz += `<span class="chip ${cls}" style="min-width:36px;">${v}</span>`;
|
||
});
|
||
viz += '</div>';
|
||
viz += `<div style="margin-top:8px;"><b>dp 表(容量 0~${s.target}):</b></div>`;
|
||
viz += '<div style="display:flex;flex-wrap:wrap;gap:3px;margin:4px 0;">';
|
||
for (let j = 0; j <= s.target; j++) {
|
||
let cls = s.dp[j] ? 'green' : 'default';
|
||
if (j === s.curJ) cls = 'orange';
|
||
viz += `<span class="chip-group"><span class="chip ${cls}" style="min-width:28px;font-size:12px;height:26px;">${s.dp[j]?'T':'F'}</span><span class="chip-index">${j}</span></span>`;
|
||
}
|
||
viz += '</div>';
|
||
$('vizArea').innerHTML = viz;
|
||
|
||
$('detailContent').innerHTML = '<div class="calc-block">' + s.msg + '</div>';
|
||
if (s.dp[s.target]) {
|
||
$('detailContent').innerHTML += `<div class="current-answer">dp[${s.target}] = <b>true</b></div>`;
|
||
}
|
||
if (s.stage === 'done') {
|
||
if (s.dp && s.dp[s.target]) {
|
||
$('resultContent').innerHTML = `<div class="final-answer">可以等分为两个子集,和各为 <b>${s.target}</b> ✅</div>`;
|
||
} else {
|
||
$('resultContent').innerHTML = '<div class="final-answer" style="border-color:#f87171;background:#fef2f2;">无法等分 ❌</div>';
|
||
}
|
||
}
|
||
$('hintText').textContent = s.msg;
|
||
const stages = ['init→初始化','item→物品','fill→填充','done→完成'];
|
||
$('pipeline').innerHTML = stages.map(st => {
|
||
const [k,l] = st.split('→');
|
||
return `<span class="pipe-step ${s.stage===k?'active':''}">${l}</span>`;
|
||
}).join('<i>→</i>');
|
||
}
|
||
|
||
function init() {
|
||
const sel = $('exampleSelect');
|
||
examples.forEach((e,i) => { sel.innerHTML += `<option value="${i}">${e.label}</option>`; });
|
||
$('inputArea').value = '[1,5,11,5]';
|
||
buildSteps(examples[0].input);
|
||
stepCtrl = new StepController({onStep: render});
|
||
stepCtrl.setSteps(steps.map((_,i)=>i));
|
||
$('stepInfo').textContent = '步骤 1 / ' + steps.length;
|
||
stepCtrl.onStep = (idx) => { render(idx); $('stepInfo').textContent = `步骤 ${idx+1} / ${steps.length}`; };
|
||
$('applyBtn').onclick = () => {
|
||
try { const arr = JSON.parse($('inputArea').value); buildSteps(arr); stepCtrl.setSteps(steps.map((_,i)=>i)); render(0); $('stepInfo').textContent = '步骤 1 / ' + steps.length; }
|
||
catch(e) { alert('请输入合法 JSON 数组'); }
|
||
};
|
||
$('exampleSelect').onchange = () => {
|
||
const e = examples[parseInt($('exampleSelect').value)];
|
||
$('inputArea').value = JSON.stringify(e.input);
|
||
buildSteps(e.input); stepCtrl.setSteps(steps.map((_,i)=>i)); render(0);
|
||
};
|
||
$('prevBtn').onclick = () => stepCtrl.prev();
|
||
$('nextBtn').onclick = () => stepCtrl.next();
|
||
$('jumpBtn').onclick = () => stepCtrl.jumpToEnd();
|
||
$('autoBtn').onclick = () => { const on = stepCtrl.toggleAuto(); $('autoBtn').textContent = on ? '暂停' : '自动播放'; };
|
||
$('resetBtn').onclick = () => { stepCtrl.reset(); $('autoBtn').textContent = '自动播放'; };
|
||
}
|
||
init();
|
||
$('codeArea').innerHTML = renderCode(`def canPartition(nums):
|
||
total = sum(nums)
|
||
if total % 2: return False
|
||
target = total // 2
|
||
dp = [False] * (target + 1)
|
||
dp[0] = True
|
||
for num in nums:
|
||
for j in range(target, num - 1, -1):
|
||
dp[j] = dp[j] or dp[j - num]
|
||
if dp[target]: return True
|
||
return dp[target]`, {lang:'Python'});
|
||
'''
|
||
|
||
|
||
def js_longest_valid_parentheses():
|
||
return r'''
|
||
const examples = [
|
||
{input: '(()', label: '示例1: "(()"'},
|
||
{input: ')()())', label: '示例2: ")()())"'},
|
||
{input: '', label: '示例3: ""'},
|
||
];
|
||
let s, steps, stepCtrl;
|
||
|
||
function buildSteps(str) {
|
||
s = str; steps = [];
|
||
if (str.length === 0) {
|
||
steps.push({stage:'done', msg:'空字符串,结果为 0', dp:[], stack:[-1], maxLen:0, current:-1, matched:-1});
|
||
return;
|
||
}
|
||
const n = str.length;
|
||
const dp = new Array(n).fill(0);
|
||
const stackArr = [-1];
|
||
let maxLen = 0;
|
||
steps.push({stage:'init', msg:'用栈方法:栈中保存索引,初始压入 -1 作为基线', dp:[...dp], stack:[-1], maxLen:0, current:-1, matched:-1});
|
||
for (let i = 0; i < n; i++) {
|
||
steps.push({stage:'check', msg:`处理 s[${i}]='${str[i]}'`, dp:[...dp], stack:[...stackArr], maxLen, current:i, matched:-1});
|
||
if (str[i] === '(') {
|
||
stackArr.push(i);
|
||
steps.push({stage:'push', msg:`遇到 '(',压入索引 ${i}`, dp:[...dp], stack:[...stackArr], maxLen, current:i, matched:-1});
|
||
} else {
|
||
if (stackArr.length > 1) {
|
||
stackArr.pop();
|
||
const start = stackArr[stackArr.length - 1];
|
||
const len = i - start;
|
||
maxLen = Math.max(maxLen, len);
|
||
dp[i] = len;
|
||
steps.push({stage:'pop', msg:`遇到 ')',弹出栈顶,栈顶=${start},有效长度=${i}-${start}=${len}`, dp:[...dp], stack:[...stackArr], maxLen, current:i, matched:start});
|
||
} else {
|
||
stackArr.length = 0;
|
||
stackArr.push(i);
|
||
steps.push({stage:'reset', msg:`遇到 ')' 但栈空(无匹配),重置基线为 ${i}`, dp:[...dp], stack:[...stackArr], maxLen, current:i, matched:-1});
|
||
}
|
||
}
|
||
}
|
||
steps.push({stage:'done', msg:`最长有效括号子串长度 = ${maxLen}`, dp:[...dp], stack:[...stackArr], maxLen, current:-1, matched:-1});
|
||
}
|
||
|
||
function render(step) {
|
||
const st = steps[step];
|
||
if (!st.dp || st.dp.length === 0) {
|
||
$('vizArea').innerHTML = '<div class="formula-box">空字符串,结果为 0</div>';
|
||
$('detailContent').innerHTML = '<div class="calc-block">' + st.msg + '</div>';
|
||
$('resultContent').innerHTML = '<div class="final-answer">最长有效括号长度 = <b>0</b></div>';
|
||
$('hintText').textContent = st.msg;
|
||
$('pipeline').innerHTML = '<span class="pipe-step active">完成</span>';
|
||
return;
|
||
}
|
||
let viz = '<div style="margin-top:8px;"><b>字符串:</b></div>';
|
||
viz += '<div style="display:flex;gap:3px;margin:8px 0;">';
|
||
for (let i = 0; i < s.length; i++) {
|
||
let cls = 'default';
|
||
if (i === st.current) cls = 'orange';
|
||
else if (st.dp[i] > 0) cls = 'green';
|
||
else if (i === st.matched) cls = 'blue';
|
||
viz += `<span class="chip ${cls}" style="min-width:28px;font-size:14px;">${s[i]}</span>`;
|
||
}
|
||
viz += '</div>';
|
||
viz += '<div style="display:flex;gap:3px;margin-top:-4px;">';
|
||
for (let i = 0; i < s.length; i++) {
|
||
viz += `<span style="min-width:28px;text-align:center;font-size:10px;color:var(--text-muted);">${i}</span>`;
|
||
}
|
||
viz += '</div>';
|
||
viz += '<div style="margin-top:8px;"><b>dp 值(以各位置结尾的有效长度):</b></div>';
|
||
viz += '<div style="display:flex;gap:3px;margin:4px 0;">';
|
||
for (let i = 0; i < s.length; i++) {
|
||
let cls = st.dp[i] > 0 ? 'green' : 'default';
|
||
if (i === st.current) cls = 'orange';
|
||
viz += `<span class="chip ${cls}" style="min-width:28px;font-size:12px;height:28px;">${st.dp[i]}</span>`;
|
||
}
|
||
viz += '</div>';
|
||
if (st.stack) {
|
||
viz += '<div style="margin-top:8px;"><b>栈:</b></div>';
|
||
viz += '<div style="display:flex;gap:3px;flex-wrap:wrap;margin:4px 0;">';
|
||
st.stack.forEach((v, idx) => {
|
||
const cls = idx === st.stack.length - 1 ? 'blue' : 'default';
|
||
viz += `<span class="chip ${cls}" style="min-width:28px;font-size:12px;height:28px;">${v}</span>`;
|
||
});
|
||
viz += '</div>';
|
||
}
|
||
$('vizArea').innerHTML = viz;
|
||
|
||
$('detailContent').innerHTML = '<div class="calc-block">' + st.msg + '</div>';
|
||
if (st.maxLen > 0) {
|
||
$('detailContent').innerHTML += `<div class="current-answer">当前最长有效长度:<b>${st.maxLen}</b></div>`;
|
||
}
|
||
if (st.stage === 'done') {
|
||
$('resultContent').innerHTML = `<div class="final-answer">最长有效括号子串长度 = <b>${st.maxLen}</b></div>`;
|
||
}
|
||
$('hintText').textContent = st.msg;
|
||
const stages = ['init→初始化','check→检查','push→入栈','pop→匹配','reset→重置','done→完成'];
|
||
$('pipeline').innerHTML = stages.map(st2 => {
|
||
const [k,l] = st2.split('→');
|
||
return `<span class="pipe-step ${st.stage===k?'active':''}">${l}</span>`;
|
||
}).join('<i>→</i>');
|
||
}
|
||
|
||
function init() {
|
||
const sel = $('exampleSelect');
|
||
examples.forEach((e,i) => { sel.innerHTML += `<option value="${i}">${e.label}</option>`; });
|
||
$('inputArea').value = '"(()"';
|
||
buildSteps(examples[0].input);
|
||
stepCtrl = new StepController({onStep: render});
|
||
stepCtrl.setSteps(steps.map((_,i)=>i));
|
||
$('stepInfo').textContent = '步骤 1 / ' + steps.length;
|
||
stepCtrl.onStep = (idx) => { render(idx); $('stepInfo').textContent = `步骤 ${idx+1} / ${steps.length}`; };
|
||
$('applyBtn').onclick = () => {
|
||
const m = $('inputArea').value.match(/"([^"]+)"/);
|
||
if (!m) { alert('格式: "(()"'); return; }
|
||
buildSteps(m[1]);
|
||
stepCtrl.setSteps(steps.map((_,i)=>i)); render(0); $('stepInfo').textContent = '步骤 1 / ' + steps.length;
|
||
};
|
||
$('exampleSelect').onchange = () => {
|
||
const e = examples[parseInt($('exampleSelect').value)];
|
||
$('inputArea').value = `"${e.input}"`;
|
||
buildSteps(e.input); stepCtrl.setSteps(steps.map((_,i)=>i)); render(0);
|
||
};
|
||
$('prevBtn').onclick = () => stepCtrl.prev();
|
||
$('nextBtn').onclick = () => stepCtrl.next();
|
||
$('jumpBtn').onclick = () => stepCtrl.jumpToEnd();
|
||
$('autoBtn').onclick = () => { const on = stepCtrl.toggleAuto(); $('autoBtn').textContent = on ? '暂停' : '自动播放'; };
|
||
$('resetBtn').onclick = () => { stepCtrl.reset(); $('autoBtn').textContent = '自动播放'; };
|
||
}
|
||
init();
|
||
$('codeArea').innerHTML = renderCode(`def longestValidParentheses(s):
|
||
stack = [-1]
|
||
max_len = 0
|
||
for i, c in enumerate(s):
|
||
if c == '(':
|
||
stack.append(i)
|
||
else:
|
||
stack.pop()
|
||
if not stack:
|
||
stack.append(i)
|
||
else:
|
||
max_len = max(max_len, i - stack[-1])
|
||
return max_len`, {lang:'Python'});
|
||
'''
|