Skip to content

Commit 66bcf15

Browse files
yanglbmeclaude
andauthored
feat: add rust solution to lc No.3093 (#5218)
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
1 parent 449263e commit 66bcf15

4 files changed

Lines changed: 321 additions & 0 deletions

File tree

.agents/skills/git-commit/SKILL.md

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
---
2+
name: git-commit
3+
description: 'Execute git commit with conventional commit message analysis, intelligent staging, and message generation. Use when user asks to commit changes, create a git commit, or mentions "/commit". Supports: (1) Auto-detecting type and scope from changes, (2) Generating conventional commit messages from diff, (3) Interactive commit with optional type/scope/description overrides, (4) Intelligent file staging for logical grouping'
4+
license: MIT
5+
allowed-tools: Bash
6+
---
7+
8+
# Git Commit with Conventional Commits
9+
10+
## Overview
11+
12+
Create standardized, semantic git commits using the Conventional Commits specification. Analyze the actual diff to determine appropriate type, scope, and message.
13+
14+
## Conventional Commit Format
15+
16+
```
17+
<type>[optional scope]: <description>
18+
19+
[optional body]
20+
21+
[optional footer(s)]
22+
```
23+
24+
## Commit Types
25+
26+
| Type | Purpose |
27+
| ---------- | ------------------------------ |
28+
| `feat` | New feature |
29+
| `fix` | Bug fix |
30+
| `docs` | Documentation only |
31+
| `style` | Formatting/style (no logic) |
32+
| `refactor` | Code refactor (no feature/fix) |
33+
| `perf` | Performance improvement |
34+
| `test` | Add/update tests |
35+
| `build` | Build system/dependencies |
36+
| `ci` | CI/config changes |
37+
| `chore` | Maintenance/misc |
38+
| `revert` | Revert commit |
39+
40+
## Breaking Changes
41+
42+
```
43+
# Exclamation mark after type/scope
44+
feat!: remove deprecated endpoint
45+
46+
# BREAKING CHANGE footer
47+
feat: allow config to extend other configs
48+
49+
BREAKING CHANGE: `extends` key behavior changed
50+
```
51+
52+
## Workflow
53+
54+
### 1. Analyze Diff
55+
56+
```bash
57+
# If files are staged, use staged diff
58+
git diff --staged
59+
60+
# If nothing staged, use working tree diff
61+
git diff
62+
63+
# Also check status
64+
git status --porcelain
65+
```
66+
67+
### 2. Stage Files (if needed)
68+
69+
If nothing is staged or you want to group changes differently:
70+
71+
```bash
72+
# Stage specific files
73+
git add path/to/file1 path/to/file2
74+
75+
# Stage by pattern
76+
git add *.test.*
77+
git add src/components/*
78+
79+
# Interactive staging
80+
git add -p
81+
```
82+
83+
**Never commit secrets** (.env, credentials.json, private keys).
84+
85+
### 3. Generate Commit Message
86+
87+
Analyze the diff to determine:
88+
89+
- **Type**: What kind of change is this?
90+
- **Scope**: What area/module is affected?
91+
- **Description**: One-line summary of what changed (present tense, imperative mood, <72 chars)
92+
93+
### 4. Execute Commit
94+
95+
```bash
96+
# Single line
97+
git commit -m "<type>[scope]: <description>"
98+
99+
# Multi-line with body/footer
100+
git commit -m "$(cat <<'EOF'
101+
<type>[scope]: <description>
102+
103+
<optional body>
104+
105+
<optional footer>
106+
EOF
107+
)"
108+
```
109+
110+
## Best Practices
111+
112+
- One logical change per commit
113+
- Present tense: "add" not "added"
114+
- Imperative mood: "fix bug" not "fixes bug"
115+
- Reference issues: `Closes #123`, `Refs #456`
116+
- Keep description under 72 characters
117+
- **Always use English for commit messages.** Never use other languages.
118+
119+
## Git Safety Protocol
120+
121+
- NEVER update git config
122+
- NEVER run destructive commands (--force, hard reset) without explicit request
123+
- NEVER skip hooks (--no-verify) unless user asks
124+
- NEVER force push to main/master
125+
- If commit fails due to hooks, fix and create NEW commit (don't amend)

solution/3000-3099/3093.Longest Common Suffix Queries/README.md

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,73 @@ function stringIndices(wordsContainer: string[], wordsQuery: string[]): number[]
382382
}
383383
```
384384

385+
#### Rust
386+
387+
```rust
388+
const INF: i32 = i32::MAX;
389+
390+
struct TrieNode {
391+
children: [Option<Box<TrieNode>>; 26],
392+
length: i32,
393+
idx: i32,
394+
}
395+
396+
impl TrieNode {
397+
fn new() -> Self {
398+
Self {
399+
children: Default::default(),
400+
length: INF,
401+
idx: INF,
402+
}
403+
}
404+
405+
fn insert(&mut self, w: &[u8], i: i32) {
406+
let len = w.len() as i32;
407+
if self.length > len {
408+
self.length = len;
409+
self.idx = i;
410+
}
411+
let mut node = self;
412+
for c in w.iter().rev() {
413+
let idx = (*c - b'a') as usize;
414+
if node.children[idx].is_none() {
415+
node.children[idx] = Some(Box::new(TrieNode::new()));
416+
}
417+
node = node.children[idx].as_mut().unwrap();
418+
if node.length > len {
419+
node.length = len;
420+
node.idx = i;
421+
}
422+
}
423+
}
424+
425+
fn query(&self, w: &[u8]) -> i32 {
426+
let mut node = self;
427+
for c in w.iter().rev() {
428+
let idx = (*c - b'a') as usize;
429+
match &node.children[idx] {
430+
Some(child) => node = child,
431+
None => break,
432+
}
433+
}
434+
node.idx
435+
}
436+
}
437+
438+
impl Solution {
439+
pub fn string_indices(words_container: Vec<String>, words_query: Vec<String>) -> Vec<i32> {
440+
let mut trie = TrieNode::new();
441+
for (i, w) in words_container.iter().enumerate() {
442+
trie.insert(w.as_bytes(), i as i32);
443+
}
444+
words_query
445+
.iter()
446+
.map(|w| trie.query(w.as_bytes()))
447+
.collect()
448+
}
449+
}
450+
```
451+
385452
<!-- tabs:end -->
386453

387454
<!-- solution:end -->

solution/3000-3099/3093.Longest Common Suffix Queries/README_EN.md

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,73 @@ function stringIndices(wordsContainer: string[], wordsQuery: string[]): number[]
380380
}
381381
```
382382

383+
#### Rust
384+
385+
```rust
386+
const INF: i32 = i32::MAX;
387+
388+
struct TrieNode {
389+
children: [Option<Box<TrieNode>>; 26],
390+
length: i32,
391+
idx: i32,
392+
}
393+
394+
impl TrieNode {
395+
fn new() -> Self {
396+
Self {
397+
children: Default::default(),
398+
length: INF,
399+
idx: INF,
400+
}
401+
}
402+
403+
fn insert(&mut self, w: &[u8], i: i32) {
404+
let len = w.len() as i32;
405+
if self.length > len {
406+
self.length = len;
407+
self.idx = i;
408+
}
409+
let mut node = self;
410+
for c in w.iter().rev() {
411+
let idx = (*c - b'a') as usize;
412+
if node.children[idx].is_none() {
413+
node.children[idx] = Some(Box::new(TrieNode::new()));
414+
}
415+
node = node.children[idx].as_mut().unwrap();
416+
if node.length > len {
417+
node.length = len;
418+
node.idx = i;
419+
}
420+
}
421+
}
422+
423+
fn query(&self, w: &[u8]) -> i32 {
424+
let mut node = self;
425+
for c in w.iter().rev() {
426+
let idx = (*c - b'a') as usize;
427+
match &node.children[idx] {
428+
Some(child) => node = child,
429+
None => break,
430+
}
431+
}
432+
node.idx
433+
}
434+
}
435+
436+
impl Solution {
437+
pub fn string_indices(words_container: Vec<String>, words_query: Vec<String>) -> Vec<i32> {
438+
let mut trie = TrieNode::new();
439+
for (i, w) in words_container.iter().enumerate() {
440+
trie.insert(w.as_bytes(), i as i32);
441+
}
442+
words_query
443+
.iter()
444+
.map(|w| trie.query(w.as_bytes()))
445+
.collect()
446+
}
447+
}
448+
```
449+
383450
<!-- tabs:end -->
384451

385452
<!-- solution:end -->
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
const INF: i32 = i32::MAX;
2+
3+
struct TrieNode {
4+
children: [Option<Box<TrieNode>>; 26],
5+
length: i32,
6+
idx: i32,
7+
}
8+
9+
impl TrieNode {
10+
fn new() -> Self {
11+
Self {
12+
children: Default::default(),
13+
length: INF,
14+
idx: INF,
15+
}
16+
}
17+
18+
fn insert(&mut self, w: &[u8], i: i32) {
19+
let len = w.len() as i32;
20+
if self.length > len {
21+
self.length = len;
22+
self.idx = i;
23+
}
24+
let mut node = self;
25+
for c in w.iter().rev() {
26+
let idx = (*c - b'a') as usize;
27+
if node.children[idx].is_none() {
28+
node.children[idx] = Some(Box::new(TrieNode::new()));
29+
}
30+
node = node.children[idx].as_mut().unwrap();
31+
if node.length > len {
32+
node.length = len;
33+
node.idx = i;
34+
}
35+
}
36+
}
37+
38+
fn query(&self, w: &[u8]) -> i32 {
39+
let mut node = self;
40+
for c in w.iter().rev() {
41+
let idx = (*c - b'a') as usize;
42+
match &node.children[idx] {
43+
Some(child) => node = child,
44+
None => break,
45+
}
46+
}
47+
node.idx
48+
}
49+
}
50+
51+
impl Solution {
52+
pub fn string_indices(words_container: Vec<String>, words_query: Vec<String>) -> Vec<i32> {
53+
let mut trie = TrieNode::new();
54+
for (i, w) in words_container.iter().enumerate() {
55+
trie.insert(w.as_bytes(), i as i32);
56+
}
57+
words_query
58+
.iter()
59+
.map(|w| trie.query(w.as_bytes()))
60+
.collect()
61+
}
62+
}

0 commit comments

Comments
 (0)