diff --git a/core-java-modules/core-java-string-algorithms-4/README.md b/core-java-modules/core-java-string-algorithms-4/README.md
new file mode 100644
index 0000000000..0651448493
--- /dev/null
+++ b/core-java-modules/core-java-string-algorithms-4/README.md
@@ -0,0 +1,5 @@
+## Java String Algorithms
+
+This module contains articles about string-related algorithms.
+
+### Relevant Articles:
\ No newline at end of file
diff --git a/core-java-modules/core-java-string-algorithms-4/pom.xml b/core-java-modules/core-java-string-algorithms-4/pom.xml
new file mode 100644
index 0000000000..b374b48743
--- /dev/null
+++ b/core-java-modules/core-java-string-algorithms-4/pom.xml
@@ -0,0 +1,14 @@
+
+ 4.0.0
+ core-java-string-algorithms-4
+ core-java-string-algorithms-4
+ jar
+
+
+ com.baeldung.core-java-modules
+ core-java-modules
+ 0.0.1-SNAPSHOT
+
+
\ No newline at end of file
diff --git a/core-java-modules/core-java-string-algorithms-4/src/test/java/com/baeldung/string/shiftchar/ShiftCharInStringUnitTest.java b/core-java-modules/core-java-string-algorithms-4/src/test/java/com/baeldung/string/shiftchar/ShiftCharInStringUnitTest.java
new file mode 100644
index 0000000000..92bbc48c98
--- /dev/null
+++ b/core-java-modules/core-java-string-algorithms-4/src/test/java/com/baeldung/string/shiftchar/ShiftCharInStringUnitTest.java
@@ -0,0 +1,100 @@
+package com.baeldung.string.shiftchar;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Test;
+
+public class ShiftCharInStringUnitTest {
+
+ private final static String STRING = "abcdefg";
+
+ private final static String EXPECT_1X = "gabcdef";
+ private final static String EXPECT_2X = "fgabcde";
+ private final static String EXPECT_3X = "efgabcd";
+ private final static String EXPECT_6X = "bcdefga";
+ private final static String EXPECT_7X = "abcdefg";
+ private final static String EXPECT_24X = "efgabcd";
+
+ private final static String B_EXPECT_1X = "bcdefga";
+ private final static String B_EXPECT_2X = "cdefgab";
+ private final static String B_EXPECT_3X = "defgabc";
+ private final static String B_EXPECT_6X = "gabcdef";
+ private final static String B_EXPECT_7X = "abcdefg";
+ private final static String B_EXPECT_24X = "defgabc";
+
+ static String rotateString1(String s, int c, boolean forward) {
+ if (c < 0) {
+ throw new IllegalArgumentException("Rotation character count cannot be negative!");
+ }
+ int len = s.length();
+ int n = c % len;
+ if (n == 0) {
+ return s;
+ }
+ n = forward ? n : len - n;
+ return s.substring(len - n, len) + s.substring(0, len - n);
+ }
+
+ static String rotateString2(String s, int c, boolean forward) {
+ if (c < 0) {
+ throw new IllegalArgumentException("Rotation character count cannot be negative!");
+ }
+ int len = s.length();
+ int n = c % len;
+ if (n == 0) {
+ return s;
+ }
+ String ss = s + s;
+ n = forward ? n : len - n;
+ return ss.substring(len - n, 2 * len - n);
+ }
+
+ static boolean rotatedFrom(String rotated, String rotateFrom) {
+ return rotateFrom.length() == rotated.length() && (rotateFrom + rotateFrom).contains(rotated);
+ }
+
+ @Test
+ void whenUsingRotateString1_thenGetExpectedResults() {
+ assertEquals(EXPECT_1X, rotateString1(STRING, 1, true));
+ assertEquals(EXPECT_2X, rotateString1(STRING, 2, true));
+ assertEquals(EXPECT_3X, rotateString1(STRING, 3, true));
+ assertEquals(EXPECT_6X, rotateString1(STRING, 6, true));
+ assertEquals(EXPECT_7X, rotateString1(STRING, 7, true));
+ assertEquals(EXPECT_24X, rotateString1(STRING, 24, true));
+
+ //backward
+ assertEquals(B_EXPECT_1X, rotateString1(STRING, 1, false));
+ assertEquals(B_EXPECT_2X, rotateString1(STRING, 2, false));
+ assertEquals(B_EXPECT_3X, rotateString1(STRING, 3, false));
+ assertEquals(B_EXPECT_6X, rotateString1(STRING, 6, false));
+ assertEquals(B_EXPECT_7X, rotateString1(STRING, 7, false));
+ assertEquals(B_EXPECT_24X, rotateString1(STRING, 24, false));
+ }
+
+ @Test
+ void whenUsingShiftString2_thenGetExpectedResults() {
+ assertEquals(EXPECT_1X, rotateString2(STRING, 1, true));
+ assertEquals(EXPECT_2X, rotateString2(STRING, 2, true));
+ assertEquals(EXPECT_3X, rotateString2(STRING, 3, true));
+ assertEquals(EXPECT_6X, rotateString2(STRING, 6, true));
+ assertEquals(EXPECT_7X, rotateString2(STRING, 7, true));
+ assertEquals(EXPECT_24X, rotateString2(STRING, 24, true));
+
+ //backward
+ assertEquals(B_EXPECT_1X, rotateString2(STRING, 1, false));
+ assertEquals(B_EXPECT_2X, rotateString2(STRING, 2, false));
+ assertEquals(B_EXPECT_3X, rotateString2(STRING, 3, false));
+ assertEquals(B_EXPECT_6X, rotateString2(STRING, 6, false));
+ assertEquals(B_EXPECT_7X, rotateString2(STRING, 7, false));
+ assertEquals(B_EXPECT_24X, rotateString2(STRING, 24, false));
+ }
+
+ @Test
+ void whenUsingRotateFrom_thenGetExpectedResults() {
+ assertTrue(rotatedFrom(EXPECT_7X, STRING));
+ assertTrue(rotatedFrom(B_EXPECT_3X, STRING));
+ assertFalse(rotatedFrom("abcefgd", STRING));
+ }
+}
\ No newline at end of file
diff --git a/core-java-modules/pom.xml b/core-java-modules/pom.xml
index f16cff3987..639a02a8cf 100644
--- a/core-java-modules/pom.xml
+++ b/core-java-modules/pom.xml
@@ -65,7 +65,6 @@
core-java-streams-5
core-java-streams-collect
core-java-streams-maps
- core-java-string-algorithms-3
core-java-string-operations-4
@@ -185,6 +184,8 @@
core-java-streams-3
core-java-string-algorithms
core-java-string-algorithms-2
+ core-java-string-algorithms-3
+ core-java-string-algorithms-4
core-java-string-apis
core-java-swing