Skip to content

Commit d355de1

Browse files
Merge branch 'master' into ddaline
2 parents 65c71a9 + f8688ba commit d355de1

File tree

106 files changed

+11438
-726
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

106 files changed

+11438
-726
lines changed

.github/workflows/codeql.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,15 @@ jobs:
3030
distribution: 'temurin'
3131

3232
- name: Initialize CodeQL
33-
uses: github/codeql-action/init@v3
33+
uses: github/codeql-action/init@v4
3434
with:
3535
languages: 'java-kotlin'
3636

3737
- name: Build
3838
run: mvn --batch-mode --update-snapshots verify
3939

4040
- name: Perform CodeQL Analysis
41-
uses: github/codeql-action/analyze@v3
41+
uses: github/codeql-action/analyze@v4
4242
with:
4343
category: "/language:java-kotlin"
4444

@@ -55,12 +55,12 @@ jobs:
5555
uses: actions/checkout@v5
5656

5757
- name: Initialize CodeQL
58-
uses: github/codeql-action/init@v3
58+
uses: github/codeql-action/init@v4
5959
with:
6060
languages: 'actions'
6161

6262
- name: Perform CodeQL Analysis
63-
uses: github/codeql-action/analyze@v3
63+
uses: github/codeql-action/analyze@v4
6464
with:
6565
category: "/language:actions"
6666
...

.gitpod.dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM gitpod/workspace-java-21:2025-08-25-18-17-39
1+
FROM gitpod/workspace-java-21:2025-10-06-13-14-25
22

33
ENV LLVM_SCRIPT="tmp_llvm.sh"
44

DIRECTORY.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@
173173
- 📄 [TarjansAlgorithm](src/main/java/com/thealgorithms/datastructures/graphs/TarjansAlgorithm.java)
174174
- 📄 [UndirectedAdjacencyListGraph](src/main/java/com/thealgorithms/datastructures/graphs/UndirectedAdjacencyListGraph.java)
175175
- 📄 [WelshPowell](src/main/java/com/thealgorithms/datastructures/graphs/WelshPowell.java)
176+
- 📄 [TwoSat](src/main/java/com/thealgorithms/datastructures/graphs/TwoSat.java)
176177
- 📁 **hashmap**
177178
- 📁 **hashing**
178179
- 📄 [GenericHashMapUsingArray](src/main/java/com/thealgorithms/datastructures/hashmap/hashing/GenericHashMapUsingArray.java)
@@ -352,6 +353,8 @@
352353
- 📄 [PredecessorConstrainedDfs](src/main/java/com/thealgorithms/graph/PredecessorConstrainedDfs.java)
353354
- 📄 [StronglyConnectedComponentOptimized](src/main/java/com/thealgorithms/graph/StronglyConnectedComponentOptimized.java)
354355
- 📄 [TravelingSalesman](src/main/java/com/thealgorithms/graph/TravelingSalesman.java)
356+
- 📄 [Dinic](src/main/java/com/thealgorithms/graph/Dinic.java)
357+
- 📄 [YensKShortestPaths](src/main/java/com/thealgorithms/graph/YensKShortestPaths.java)
355358
- 📁 **greedyalgorithms**
356359
- 📄 [ActivitySelection](src/main/java/com/thealgorithms/greedyalgorithms/ActivitySelection.java)
357360
- 📄 [BandwidthAllocation](src/main/java/com/thealgorithms/greedyalgorithms/BandwidthAllocation.java)

pmd-exclude.properties

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,12 +88,14 @@ com.thealgorithms.others.LinearCongruentialGenerator=UselessMainMethod
8888
com.thealgorithms.others.Luhn=UnnecessaryFullyQualifiedName,UselessMainMethod
8989
com.thealgorithms.others.Mandelbrot=UselessMainMethod,UselessParentheses
9090
com.thealgorithms.others.MiniMaxAlgorithm=UselessMainMethod,UselessParentheses
91+
com.thealgorithms.others.MosAlgorithm=UselessMainMethod
9192
com.thealgorithms.others.PageRank=UselessMainMethod,UselessParentheses
9293
com.thealgorithms.others.PerlinNoise=UselessMainMethod,UselessParentheses
9394
com.thealgorithms.others.QueueUsingTwoStacks=UselessParentheses
9495
com.thealgorithms.others.Trieac=UselessMainMethod,UselessParentheses
9596
com.thealgorithms.others.Verhoeff=UnnecessaryFullyQualifiedName,UselessMainMethod
9697
com.thealgorithms.puzzlesandgames.Sudoku=UselessMainMethod
98+
com.thealgorithms.recursion.DiceThrower=UselessMainMethod
9799
com.thealgorithms.searches.HowManyTimesRotated=UselessMainMethod
98100
com.thealgorithms.searches.InterpolationSearch=UselessParentheses
99101
com.thealgorithms.searches.KMPSearch=UselessParentheses

pom.xml

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,7 @@
7171
<artifactId>maven-compiler-plugin</artifactId>
7272
<version>3.14.1</version>
7373
<configuration>
74-
<source>21</source>
75-
<target>21</target>
74+
<release>21</release>
7675
<compilerArgs>
7776
<arg>-Xlint:all</arg>
7877
<arg>-Xlint:-auxiliaryclass</arg>
@@ -83,7 +82,7 @@
8382
<plugin>
8483
<groupId>org.jacoco</groupId>
8584
<artifactId>jacoco-maven-plugin</artifactId>
86-
<version>0.8.13</version>
85+
<version>0.8.14</version>
8786
<executions>
8887
<execution>
8988
<goals>
@@ -113,7 +112,7 @@
113112
<dependency>
114113
<groupId>com.puppycrawl.tools</groupId>
115114
<artifactId>checkstyle</artifactId>
116-
<version>11.1.0</version>
115+
<version>12.0.1</version>
117116
</dependency>
118117
</dependencies>
119118
</plugin>
@@ -128,7 +127,7 @@
128127
<plugin>
129128
<groupId>com.mebigfatguy.fb-contrib</groupId>
130129
<artifactId>fb-contrib</artifactId>
131-
<version>7.6.14</version>
130+
<version>7.6.15</version>
132131
</plugin>
133132
<plugin>
134133
<groupId>com.h3xstream.findsecbugs</groupId>
@@ -141,7 +140,7 @@
141140
<plugin>
142141
<groupId>org.apache.maven.plugins</groupId>
143142
<artifactId>maven-pmd-plugin</artifactId>
144-
<version>3.27.0</version>
143+
<version>3.28.0</version>
145144
<configuration>
146145
<rulesets>
147146
<ruleset>/rulesets/java/maven-pmd-plugin-default.xml</ruleset>
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
package com.thealgorithms.ciphers;
2+
3+
import java.util.HashSet;
4+
import java.util.Set;
5+
6+
/**
7+
* A Java implementation of Permutation Cipher.
8+
* It is a type of transposition cipher in which the plaintext is divided into blocks
9+
* and the characters within each block are rearranged according to a fixed permutation key.
10+
*
11+
* For example, with key {3, 1, 2} and plaintext "HELLO", the text is divided into blocks
12+
* of 3 characters: "HEL" and "LO" (with padding). The characters are then rearranged
13+
* according to the key positions.
14+
*
15+
* @author GitHub Copilot
16+
*/
17+
public class PermutationCipher {
18+
19+
private static final char PADDING_CHAR = 'X';
20+
21+
/**
22+
* Encrypts the given plaintext using the permutation cipher with the specified key.
23+
*
24+
* @param plaintext the text to encrypt
25+
* @param key the permutation key (array of integers representing positions)
26+
* @return the encrypted text
27+
* @throws IllegalArgumentException if the key is invalid
28+
*/
29+
public String encrypt(String plaintext, int[] key) {
30+
validateKey(key);
31+
32+
if (plaintext == null || plaintext.isEmpty()) {
33+
return plaintext;
34+
}
35+
36+
// Remove spaces and convert to uppercase for consistent processing
37+
String cleanText = plaintext.replaceAll("\\s+", "").toUpperCase();
38+
39+
// Pad the text to make it divisible by key length
40+
String paddedText = padText(cleanText, key.length);
41+
42+
StringBuilder encrypted = new StringBuilder();
43+
44+
// Process text in blocks of key length
45+
for (int i = 0; i < paddedText.length(); i += key.length) {
46+
String block = paddedText.substring(i, Math.min(i + key.length, paddedText.length()));
47+
encrypted.append(permuteBlock(block, key));
48+
}
49+
50+
return encrypted.toString();
51+
}
52+
53+
/**
54+
* Decrypts the given ciphertext using the permutation cipher with the specified key.
55+
*
56+
* @param ciphertext the text to decrypt
57+
* @param key the permutation key (array of integers representing positions)
58+
* @return the decrypted text
59+
* @throws IllegalArgumentException if the key is invalid
60+
*/
61+
public String decrypt(String ciphertext, int[] key) {
62+
validateKey(key);
63+
64+
if (ciphertext == null || ciphertext.isEmpty()) {
65+
return ciphertext;
66+
}
67+
68+
// Create the inverse permutation
69+
int[] inverseKey = createInverseKey(key);
70+
71+
StringBuilder decrypted = new StringBuilder();
72+
73+
// Process text in blocks of key length
74+
for (int i = 0; i < ciphertext.length(); i += key.length) {
75+
String block = ciphertext.substring(i, Math.min(i + key.length, ciphertext.length()));
76+
decrypted.append(permuteBlock(block, inverseKey));
77+
}
78+
79+
// Remove padding characters from the end
80+
return removePadding(decrypted.toString());
81+
}
82+
/**
83+
* Validates that the permutation key is valid.
84+
* A valid key must contain all integers from 1 to n exactly once, where n is the key length.
85+
*
86+
* @param key the permutation key to validate
87+
* @throws IllegalArgumentException if the key is invalid
88+
*/
89+
private void validateKey(int[] key) {
90+
if (key == null || key.length == 0) {
91+
throw new IllegalArgumentException("Key cannot be null or empty");
92+
}
93+
94+
Set<Integer> keySet = new HashSet<>();
95+
for (int position : key) {
96+
if (position < 1 || position > key.length) {
97+
throw new IllegalArgumentException("Key must contain integers from 1 to " + key.length);
98+
}
99+
if (!keySet.add(position)) {
100+
throw new IllegalArgumentException("Key must contain each position exactly once");
101+
}
102+
}
103+
}
104+
105+
/**
106+
* Pads the text with padding characters to make its length divisible by the block size.
107+
*
108+
* @param text the text to pad
109+
* @param blockSize the size of each block
110+
* @return the padded text
111+
*/
112+
private String padText(String text, int blockSize) {
113+
int remainder = text.length() % blockSize;
114+
if (remainder == 0) {
115+
return text;
116+
}
117+
118+
int paddingNeeded = blockSize - remainder;
119+
StringBuilder padded = new StringBuilder(text);
120+
for (int i = 0; i < paddingNeeded; i++) {
121+
padded.append(PADDING_CHAR);
122+
}
123+
124+
return padded.toString();
125+
}
126+
/**
127+
* Applies the permutation to a single block of text.
128+
*
129+
* @param block the block to permute
130+
* @param key the permutation key
131+
* @return the permuted block
132+
*/
133+
private String permuteBlock(String block, int[] key) {
134+
if (block.length() != key.length) {
135+
// Handle case where block is shorter than key (shouldn't happen with proper padding)
136+
block = padText(block, key.length);
137+
}
138+
139+
char[] result = new char[key.length];
140+
char[] blockChars = block.toCharArray();
141+
142+
for (int i = 0; i < key.length; i++) {
143+
// Key positions are 1-based, so subtract 1 for 0-based array indexing
144+
result[i] = blockChars[key[i] - 1];
145+
}
146+
147+
return new String(result);
148+
}
149+
150+
/**
151+
* Creates the inverse permutation key for decryption.
152+
*
153+
* @param key the original permutation key
154+
* @return the inverse key
155+
*/
156+
private int[] createInverseKey(int[] key) {
157+
int[] inverse = new int[key.length];
158+
159+
for (int i = 0; i < key.length; i++) {
160+
// The inverse key maps each position to where it should go
161+
inverse[key[i] - 1] = i + 1;
162+
}
163+
164+
return inverse;
165+
}
166+
167+
/**
168+
* Removes padding characters from the end of the decrypted text.
169+
*
170+
* @param text the text to remove padding from
171+
* @return the text without padding
172+
*/
173+
private String removePadding(String text) {
174+
if (text.isEmpty()) {
175+
return text;
176+
}
177+
178+
int i = text.length() - 1;
179+
while (i >= 0 && text.charAt(i) == PADDING_CHAR) {
180+
i--;
181+
}
182+
183+
return text.substring(0, i + 1);
184+
}
185+
186+
/**
187+
* Gets the padding character used by this cipher.
188+
*
189+
* @return the padding character
190+
*/
191+
public char getPaddingChar() {
192+
return PADDING_CHAR;
193+
}
194+
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
package com.thealgorithms.compression;
2+
3+
/**
4+
* An implementation of the Run-Length Encoding (RLE) algorithm.
5+
*
6+
* <p>Run-Length Encoding is a simple form of lossless data compression in which
7+
* runs of data (sequences in which the same data value occurs in many
8+
* consecutive data elements) are stored as a single data value and count,
9+
* rather than as the original run.
10+
*
11+
* <p>This implementation provides methods for both compressing and decompressing
12+
* a string. For example:
13+
* <ul>
14+
* <li>Compressing "AAAABBBCCDAA" results in "4A3B2C1D2A".</li>
15+
* <li>Decompressing "4A3B2C1D2A" results in "AAAABBBCCDAA".</li>
16+
* </ul>
17+
*
18+
* <p>Time Complexity: O(n) for both compression and decompression, where n is the
19+
* length of the input string.
20+
*
21+
* <p>References:
22+
* <ul>
23+
* <li><a href="https://en.wikipedia.org/wiki/Run-length_encoding">Wikipedia: Run-length encoding</a></li>
24+
* </ul>
25+
*/
26+
public final class RunLengthEncoding {
27+
28+
/**
29+
* Private constructor to prevent instantiation of this utility class.
30+
*/
31+
private RunLengthEncoding() {
32+
}
33+
34+
/**
35+
* Compresses a string using the Run-Length Encoding algorithm.
36+
*
37+
* @param text The string to be compressed. Must not be null.
38+
* @return The compressed string. Returns an empty string if the input is empty.
39+
*/
40+
public static String compress(String text) {
41+
if (text == null || text.isEmpty()) {
42+
return "";
43+
}
44+
45+
StringBuilder compressed = new StringBuilder();
46+
int count = 1;
47+
48+
for (int i = 0; i < text.length(); i++) {
49+
// Check if it's the last character or if the next character is different
50+
if (i == text.length() - 1 || text.charAt(i) != text.charAt(i + 1)) {
51+
compressed.append(count);
52+
compressed.append(text.charAt(i));
53+
count = 1; // Reset count for the new character
54+
} else {
55+
count++;
56+
}
57+
}
58+
return compressed.toString();
59+
}
60+
61+
/**
62+
* Decompresses a string that was compressed using the Run-Length Encoding algorithm.
63+
*
64+
* @param compressedText The compressed string. Must not be null.
65+
* @return The original, uncompressed string.
66+
*/
67+
public static String decompress(String compressedText) {
68+
if (compressedText == null || compressedText.isEmpty()) {
69+
return "";
70+
}
71+
72+
StringBuilder decompressed = new StringBuilder();
73+
int count = 0;
74+
75+
for (char ch : compressedText.toCharArray()) {
76+
if (Character.isDigit(ch)) {
77+
// Build the number for runs of 10 or more (e.g., "12A")
78+
count = count * 10 + ch - '0';
79+
} else {
80+
// Append the character 'count' times
81+
decompressed.append(String.valueOf(ch).repeat(Math.max(0, count)));
82+
count = 0; // Reset count for the next sequence
83+
}
84+
}
85+
return decompressed.toString();
86+
}
87+
}

0 commit comments

Comments
 (0)