Computing All Valid IP Addresses From Raw IP String - java

I'm now solving leetcode problem 93. Restore IP Addresses.
Here's the url link: https://leetcode.com/problems/restore-ip-addresses/
The description looks like this:
Given a string s containing only digits. Return all possible valid IP addresses that can be obtained from s. You can return them in any order.
A valid IP address consists of exactly four integers, each integer is between 0 and 255, separated by single points and cannot have leading zeros. For example, "0.1.2.201" and "192.168.1.1" are valid IP addresses and "0.011.255.245", "192.168.1.312" and "192.168#1.1" are invalid IP addresses.
However, as I was trying to solve my problem via backtracking, I couldn't figure out the reason that I'm always returning an empty ArrayList. I double checked my base case and my recursion and still couldn't find the bug. Any help would be greatly appreciated, thank you!
public List<String> restoreIpAddresses(String s) {
List<String> res = new ArrayList<>();
if(s.length() == 0){
return res;
}
int[] path = new int[4];
snapshotIP(res,s,0,path,0);
return res;
}
public void snapshotIP(List<String> res, String s, int index, int[] path, int segment){
if(segment == 4 && index == s.length()){
res.add(path[0]+"."+path[1]+"."+path[2]+"."+path[3]);
return;
}
else if(segment == 4 || index == s.length()){
return;
}
for(int len = 1; len <= 3 && index + len <= s.length(); len++){
String snap = s.substring(index,index+len);
int val = Integer.parseInt(snap);
if(val > 225 || len >= 2 && s.charAt(index) == '0'){
break;
}
path[segment] = val;
snapshotIP(res,s,index+len,path,segment+1);
path[segment] = -1; //undo the choice
}
}

You have written a pretty advanced code. It's working for all the cases where IP address segment is lower than 225, but the first test case has 255s in there.
The fix is trivial, just replace "val > 225" to "val > 255".
It should be like this:
if(val > 255 || len >= 2 && s.charAt(index) == '0')
P.S.
I would do this differently, I would add dots into every possible place and validate every received combination.

Your code looks pretty good, not bad at all, not sure where your bug is though.
Here is an alternative solution, not so pretty though, it would pass just fine:
public final class Solution {
public static final List<String> restoreIpAddresses(
final String ip
) {
List<String> res = new ArrayList<>();
int length = ip.length();
for (int i = 1; i < 4 && i < length - 2; i++)
for (int j = i + 1; j < i + 4 && j < length - 1; j++)
for (int k = j + 1; k < j + 4 && k < length; k++) {
final String part1 = ip.substring(0, i);
final String part2 = ip.substring(i, j);
final String part3 = ip.substring(j, k);
final String part4 = ip.substring(k, length);
if (isValid(part1) && isValid(part2) && isValid(part3) && isValid(part4)) {
res.add(part1 + "." + part2 + "." + part3 + "." + part4);
}
}
return res;
}
private static final boolean isValid(
final String s
) {
if (s.length() > 3 || s.length() == 0 || (s.charAt(0) == '0' && s.length() > 1) || Integer.parseInt(s) > 255) {
return false;
}
return true;
}
}
Something a bit suspicious in your code is that the backtracking helper function is void, maybe you've to define a variable to make it work, still unsure.
Similarly in C++, if you'd be interested:
// The following block might slightly improve the execution time;
// Can be removed;
static const auto __optimize__ = []() {
std::ios::sync_with_stdio(false);
std::cin.tie(NULL);
std::cout.tie(NULL);
return 0;
}();
// Most of headers are already included;
// Can be removed;
#include <cstdint>
#include <vector>
#include <string>
#define LIMIT 256
using ValueType = std::uint_fast16_t;
static const struct Solution {
static const std::vector<std::string> restoreIpAddresses(
const std::string s
) {
const ValueType len = std::size(s);
std::vector<std::string> ips;
std::string ip;
ValueType a, b, c, d;
ValueType A, B, C, D;
for (a = 1; a < 4; ++a) {
for (b = 1; b < 4; ++b) {
for (c = 1; c < 4; ++c) {
for (d = 1; d < 4; ++d) {
if (a + b + c + d == len) {
A = std::stoi(s.substr(0, a));
B = std::stoi(s.substr(a, b));
C = std::stoi(s.substr(a + b, c));
D = std::stoi(s.substr(a + b + c, d));
if (A < LIMIT && B < LIMIT && C < LIMIT && D < LIMIT) {
ip = std::to_string(A) + "." +
std::to_string(B) + "." +
std::to_string(C) + "." +
std::to_string(D);
if (std::size(ip) == len + 3) {
ips.emplace_back(ip);
}
}
}
}
}
}
}
return ips;
}
};
Here is LeetCode's backtracking depth first search algorithm that is similar to yours, that might help you figure it out.
class Solution {
int n;
String s;
LinkedList<String> segments = new LinkedList<String>();
ArrayList<String> output = new ArrayList<String>();
public boolean valid(String segment) {
/*
Check if the current segment is valid :
1. less or equal to 255
2. the first character could be '0'
only if the segment is equal to '0'
*/
int m = segment.length();
if (m > 3)
return false;
return (segment.charAt(0) != '0') ? (Integer.valueOf(segment) <= 255) : (m == 1);
}
public void update_output(int curr_pos) {
/*
Append the current list of segments
to the list of solutions
*/
String segment = s.substring(curr_pos + 1, n);
if (valid(segment)) {
segments.add(segment);
output.add(String.join(".", segments));
segments.removeLast();
}
}
public void backtrack(int prev_pos, int dots) {
/*
prev_pos : the position of the previously placed dot
dots : number of dots to place
*/
// The current dot curr_pos could be placed
// in a range from prev_pos + 1 to prev_pos + 4.
// The dot couldn't be placed
// after the last character in the string.
int max_pos = Math.min(n - 1, prev_pos + 4);
for (int curr_pos = prev_pos + 1; curr_pos < max_pos; curr_pos++) {
String segment = s.substring(prev_pos + 1, curr_pos + 1);
if (valid(segment)) {
segments.add(segment); // place dot
if (dots - 1 == 0) // if all 3 dots are placed
update_output(curr_pos); // add the solution to output
else
backtrack(curr_pos, dots - 1); // continue to place dots
segments.removeLast(); // remove the last placed dot
}
}
}
public List<String> restoreIpAddresses(String s) {
n = s.length();
this.s = s;
backtrack(-1, 3);
return output;
}
}
References
For additional details, please see the Discussion Board where you can find plenty of well-explained accepted solutions with a variety of languages including low-complexity algorithms and asymptotic runtime/memory analysis1, 2.

internal static IEnumerable<string> FetchPossibleIPs(string input, int currentSection = 1)
{
List<string> possibleIPs = new List<string>();
if (input.Length > 0)
{
// If section is 4 then no need
// to break further and simply verify.
if (currentSection == 4)
{
if (int.Parse(input) <= 255 && input[0].ToString() != "0")
{
possibleIPs.Add(input);
}
}
// Else if section is < 4 then break the string
// with substring of length of 1,2 or 3 to
// figure out possible combinations.
else
{
for (int i = 1; i <= 3; ++i)
{
var section = input.Substring(0, i);
if (int.Parse(section) <= 255 && section[0].ToString() != "0")
{
var otherSections = FetchPossibleIPs(input.Substring(i), currentSection + 1);
foreach (var item in otherSections)
{
possibleIPs.Add($"{section}.{item}");
}
}
}
}
}
return possibleIPs;
}
A sample solution in C# using recursion to solve it.
It uses recursion and backtracking to solve the problem.

Related

Efficient way to find longest streak of characters in string

This code works fine but I'm looking for a way to optimize it. If you look at the long string, you can see 'l' appears five times consecutively. No other character appears this many times consecutively. So, the output is 5. Now, the problem is this method checks each and every character and even after the max is found, it continues to check the remaining characters. Is there a more efficient way?
public class Main {
public static void main(String[] args) {
System.out.println(longestStreak("KDDiiigllllldddfnnlleeezzeddd"));
}
private static int longestStreak(String str) {
int max = 0;
for (int i = 0; i < str.length(); i++) {
int count = 0;
for (int j = i; j < str.length(); j++) {
if (str.charAt(i) == str.charAt(j)) {
count++;
} else break;
}
if (count > max) max = count;
}
return max;
}
}
We could add variable for previous char count in single iteration. Also as an additional optimisation we stop iteration if i + max - currentLenght < str.length(). It means that max can not be changed:
private static int longestStreak(String str) {
int maxLenght = 0;
int currentLenght = 1;
char prev = str.charAt(0);
for (int index = 1; index < str.length() && isMaxCanBeChanged(str, maxLenght, currentLenght, index); index++) {
char currentChar = str.charAt(index);
if (currentChar == prev) {
currentLenght++;
} else {
maxLenght = Math.max(maxLenght, currentLenght);
currentLenght = 1;
}
prev = currentChar;
}
return Math.max(maxLenght, currentLenght);
}
private static boolean isMaxCanBeChanged(String str, int max, int currentLenght, int index) {
return index + max - currentLenght < str.length();
}
Here is a regex magic solution, which although a bit brute force perhaps gets some brownie points. We can iterate starting with the number of characters in the original input, decreasing by one at a time, trying to do a regex replacement of continuous characters of that length. If the replacement works, then we know we found the longest streak.
String input = "KDDiiigllllldddfnnlleeezzeddd";
for (int i=input.length(); i > 0; --i) {
String replace = input.replaceAll(".*?(.)(\\1{" + (i-1) + "}).*", "$1");
if (replace.length() != input.length()) {
System.out.println("longest streak is: " + replace);
}
}
This prints:
longest streak is: lllll
Yes there is. C++ code:
string str = "KDDiiigllllldddfnnlleeezzeddd";
int longest_streak = 1, current_streak = 1; char longest_letter = str[0];
for (int i = 1; i < str.size(); ++i) {
if (str[i] == str[i - 1])
current_streak++;
else current_streak = 1;
if (current_streak > longest_streak) {
longest_streak = current_streak;
longest_letter = str[i];
}
}
cout << "The longest streak is: " << longest_streak << " and the character is: " << longest_letter << "\n";
LE: If needed, I can provide the Java code for it, but I think you get the idea.
public class Main {
public static void main(String[] args) {
System.out.println(longestStreak("KDDiiigllllldddfnnlleeezzeddd"));
}
private static int longestStreak(String str) {
int longest_streak = 1, current_streak = 1; char longest_letter = str.charAt(0);
for (int i = 1; i < str.length(); ++i) {
if (str.charAt(i) == str.charAt(i - 1))
current_streak++;
else current_streak = 1;
if (current_streak > longest_streak) {
longest_streak = current_streak;
longest_letter = str.charAt(i);
}
}
return longest_streak;
}
}
The loop could be rewritten a bit smaller, but mainly the condition can be optimized:
i < str.length() - max
Using Stream and collector. It should give all highest repeated elements.
Code:
String lineString = "KDDiiiiiiigllllldddfnnlleeezzeddd";
String[] lineSplit = lineString.split("");
Map<String, Integer> collect = Arrays.stream(lineSplit)
.collect(Collectors.groupingBy(Function.identity(), Collectors.summingInt(e -> 1)));
int maxValueInMap = (Collections.max(collect.values()));
for (Entry<String, Integer> entry : collect.entrySet()) {
if (entry.getValue() == maxValueInMap) {
System.out.printf("Character: %s, Repetition: %d\n", entry.getKey(), entry.getValue());
}
}
Output:
Character: i, Repetition: 7
Character: l, Repetition: 7
P.S I am not sure how efficient this code it. I just learned Streams.

Highly effect find a byte[] is contained by another byte[] [duplicate]

Given a byte array, how can I find within it, the position of a (smaller) byte array?
This documentation looked promising, using ArrayUtils, but if I'm correct it would only let me find an individual byte within the array to be searched.
(I can't see it mattering, but just in case: sometimes the search byte array will be regular ASCII characters, other times it will be control characters or extended ASCII characters. So using String operations would not always be appropriate)
The large array could be between 10 and about 10000 bytes, and the smaller array around 10. In some cases I will have several smaller arrays that I want found within the larger array in a single search. And I will at times want to find the last index of an instance rather than the first.
The simpelst way would be to compare each element:
public int indexOf(byte[] outerArray, byte[] smallerArray) {
for(int i = 0; i < outerArray.length - smallerArray.length+1; ++i) {
boolean found = true;
for(int j = 0; j < smallerArray.length; ++j) {
if (outerArray[i+j] != smallerArray[j]) {
found = false;
break;
}
}
if (found) return i;
}
return -1;
}
Some tests:
#Test
public void testIndexOf() {
byte[] outer = {1, 2, 3, 4};
assertEquals(0, indexOf(outer, new byte[]{1, 2}));
assertEquals(1, indexOf(outer, new byte[]{2, 3}));
assertEquals(2, indexOf(outer, new byte[]{3, 4}));
assertEquals(-1, indexOf(outer, new byte[]{4, 4}));
assertEquals(-1, indexOf(outer, new byte[]{4, 5}));
assertEquals(-1, indexOf(outer, new byte[]{4, 5, 6, 7, 8}));
}
As you updated your question: Java Strings are UTF-16 Strings, they do not care about the extended ASCII set, so you could use string.indexOf()
Google's Guava provides a Bytes.indexOf(byte[] array, byte[] target).
Using the Knuth–Morris–Pratt algorithm is the most efficient way.
StreamSearcher.java is an implementation of it and is part of Twitter's elephant-bird project.
It is not recommended to include this library since it is rather sizable for using just a single class.
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
/**
* An efficient stream searching class based on the Knuth-Morris-Pratt algorithm.
* For more on the algorithm works see: http://www.inf.fh-flensburg.de/lang/algorithmen/pattern/kmpen.htm.
*/
public class StreamSearcher
{
private byte[] pattern_;
private int[] borders_;
// An upper bound on pattern length for searching. Results are undefined for longer patterns.
#SuppressWarnings("unused")
public static final int MAX_PATTERN_LENGTH = 1024;
StreamSearcher(byte[] pattern)
{
setPattern(pattern);
}
/**
* Sets a new pattern for this StreamSearcher to use.
*
* #param pattern the pattern the StreamSearcher will look for in future calls to search(...)
*/
public void setPattern(byte[] pattern)
{
pattern_ = Arrays.copyOf(pattern, pattern.length);
borders_ = new int[pattern_.length + 1];
preProcess();
}
/**
* Searches for the next occurrence of the pattern in the stream, starting from the current stream position. Note
* that the position of the stream is changed. If a match is found, the stream points to the end of the match -- i.e. the
* byte AFTER the pattern. Else, the stream is entirely consumed. The latter is because InputStream semantics make it difficult to have
* another reasonable default, i.e. leave the stream unchanged.
*
* #return bytes consumed if found, -1 otherwise.
*/
long search(InputStream stream) throws IOException
{
long bytesRead = 0;
int b;
int j = 0;
while ((b = stream.read()) != -1)
{
bytesRead++;
while (j >= 0 && (byte) b != pattern_[j])
{
j = borders_[j];
}
// Move to the next character in the pattern.
++j;
// If we've matched up to the full pattern length, we found it. Return,
// which will automatically save our position in the InputStream at the point immediately
// following the pattern match.
if (j == pattern_.length)
{
return bytesRead;
}
}
// No dice, Note that the stream is now completely consumed.
return -1;
}
/**
* Builds up a table of longest "borders" for each prefix of the pattern to find. This table is stored internally
* and aids in implementation of the Knuth-Moore-Pratt string search.
* <p>
* For more information, see: http://www.inf.fh-flensburg.de/lang/algorithmen/pattern/kmpen.htm.
*/
private void preProcess()
{
int i = 0;
int j = -1;
borders_[i] = j;
while (i < pattern_.length)
{
while (j >= 0 && pattern_[i] != pattern_[j])
{
j = borders_[j];
}
borders_[++i] = ++j;
}
}
}
Is this what you are looking for?
public class KPM {
/**
* Search the data byte array for the first occurrence of the byte array pattern within given boundaries.
* #param data
* #param start First index in data
* #param stop Last index in data so that stop-start = length
* #param pattern What is being searched. '*' can be used as wildcard for "ANY character"
* #return
*/
public static int indexOf( byte[] data, int start, int stop, byte[] pattern) {
if( data == null || pattern == null) return -1;
int[] failure = computeFailure(pattern);
int j = 0;
for( int i = start; i < stop; i++) {
while (j > 0 && ( pattern[j] != '*' && pattern[j] != data[i])) {
j = failure[j - 1];
}
if (pattern[j] == '*' || pattern[j] == data[i]) {
j++;
}
if (j == pattern.length) {
return i - pattern.length + 1;
}
}
return -1;
}
/**
* Computes the failure function using a boot-strapping process,
* where the pattern is matched against itself.
*/
private static int[] computeFailure(byte[] pattern) {
int[] failure = new int[pattern.length];
int j = 0;
for (int i = 1; i < pattern.length; i++) {
while (j>0 && pattern[j] != pattern[i]) {
j = failure[j - 1];
}
if (pattern[j] == pattern[i]) {
j++;
}
failure[i] = j;
}
return failure;
}
}
To save your time in testing:
http://helpdesk.objects.com.au/java/search-a-byte-array-for-a-byte-sequence
gives you code that works if you make computeFailure() static:
public class KPM {
/**
* Search the data byte array for the first occurrence
* of the byte array pattern.
*/
public static int indexOf(byte[] data, byte[] pattern) {
int[] failure = computeFailure(pattern);
int j = 0;
for (int i = 0; i < data.length; i++) {
while (j > 0 && pattern[j] != data[i]) {
j = failure[j - 1];
}
if (pattern[j] == data[i]) {
j++;
}
if (j == pattern.length) {
return i - pattern.length + 1;
}
}
return -1;
}
/**
* Computes the failure function using a boot-strapping process,
* where the pattern is matched against itself.
*/
private static int[] computeFailure(byte[] pattern) {
int[] failure = new int[pattern.length];
int j = 0;
for (int i = 1; i < pattern.length; i++) {
while (j>0 && pattern[j] != pattern[i]) {
j = failure[j - 1];
}
if (pattern[j] == pattern[i]) {
j++;
}
failure[i] = j;
}
return failure;
}
}
Since it is always wise to test the code that you borrow, you may start with:
public class Test {
public static void main(String[] args) {
do_test1();
}
static void do_test1() {
String[] ss = { "",
"\r\n\r\n",
"\n\n",
"\r\n\r\nthis is a test",
"this is a test\r\n\r\n",
"this is a test\r\n\r\nthis si a test",
"this is a test\r\n\r\nthis si a test\r\n\r\n",
"this is a test\n\r\nthis si a test",
"this is a test\r\nthis si a test\r\n\r\n",
"this is a test"
};
for (String s: ss) {
System.out.println(""+KPM.indexOf(s.getBytes(), "\r\n\r\n".getBytes())+"in ["+s+"]");
}
}
}
Copied almost identical from java.lang.String.
indexOf(char[],int,int,char[]int,int,int)
static int indexOf(byte[] source, int sourceOffset, int sourceCount, byte[] target, int targetOffset, int targetCount, int fromIndex) {
if (fromIndex >= sourceCount) {
return (targetCount == 0 ? sourceCount : -1);
}
if (fromIndex < 0) {
fromIndex = 0;
}
if (targetCount == 0) {
return fromIndex;
}
byte first = target[targetOffset];
int max = sourceOffset + (sourceCount - targetCount);
for (int i = sourceOffset + fromIndex; i <= max; i++) {
/* Look for first character. */
if (source[i] != first) {
while (++i <= max && source[i] != first)
;
}
/* Found first character, now look at the rest of v2 */
if (i <= max) {
int j = i + 1;
int end = j + targetCount - 1;
for (int k = targetOffset + 1; j < end && source[j] == target[k]; j++, k++)
;
if (j == end) {
/* Found whole string. */
return i - sourceOffset;
}
}
}
return -1;
}
package org.example;
import java.util.List;
import org.riversun.finbin.BinarySearcher;
public class Sample2 {
public static void main(String[] args) throws Exception {
BinarySearcher bs = new BinarySearcher();
// UTF-8 without BOM
byte[] srcBytes = "Hello world.It's a small world.".getBytes("utf-8");
byte[] searchBytes = "world".getBytes("utf-8");
List<Integer> indexList = bs.searchBytes(srcBytes, searchBytes);
System.out.println("indexList=" + indexList);
}
}
so it results in
indexList=[6, 25]
So,u can find the index of byte[] in byte[]
Example here on Github at: https://github.com/riversun/finbin
Several (or all?) of the examples posted here failed some Unit tests so I am posting my version along with the aforementioned tests over here. All of the Unit tests are BASED upon the requirement that Java's String.indexOf() always gives us the right answer!
// The Knuth, Morris, and Pratt string searching algorithm remembers information about
// the past matched characters instead of matching a character with a different pattern
// character over and over again. It can search for a pattern in O(n) time as it never
// re-compares a text symbol that has matched a pattern symbol. But, it does use a partial
// match table to analyze the pattern structure. Construction of a partial match table
// takes O(m) time. Therefore, the overall time complexity of the KMP algorithm is O(m + n).
public class KMPSearch {
public static int indexOf(byte[] haystack, byte[] needle)
{
// needle is null or empty
if (needle == null || needle.length == 0)
return 0;
// haystack is null, or haystack's length is less than that of needle
if (haystack == null || needle.length > haystack.length)
return -1;
// pre construct failure array for needle pattern
int[] failure = new int[needle.length];
int n = needle.length;
failure[0] = -1;
for (int j = 1; j < n; j++)
{
int i = failure[j - 1];
while ((needle[j] != needle[i + 1]) && i >= 0)
i = failure[i];
if (needle[j] == needle[i + 1])
failure[j] = i + 1;
else
failure[j] = -1;
}
// find match
int i = 0, j = 0;
int haystackLen = haystack.length;
int needleLen = needle.length;
while (i < haystackLen && j < needleLen)
{
if (haystack[i] == needle[j])
{
i++;
j++;
}
else if (j == 0)
i++;
else
j = failure[j - 1] + 1;
}
return ((j == needleLen) ? (i - needleLen) : -1);
}
}
import java.util.Random;
class KMPSearchTest {
private static Random random = new Random();
private static String alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
#Test
public void testEmpty() {
test("", "");
test("", "ab");
}
#Test
public void testOneChar() {
test("a", "a");
test("a", "b");
}
#Test
public void testRepeat() {
test("aaa", "aaaaa");
test("aaa", "abaaba");
test("abab", "abacababc");
test("abab", "babacaba");
}
#Test
public void testPartialRepeat() {
test("aaacaaaaac", "aaacacaacaaacaaaacaaaaac");
test("ababcababdabababcababdaba", "ababcababdabababcababdaba");
}
#Test
public void testRandomly() {
for (int i = 0; i < 1000; i++) {
String pattern = randomPattern();
for (int j = 0; j < 100; j++)
test(pattern, randomText(pattern));
}
}
/* Helper functions */
private static String randomPattern() {
StringBuilder sb = new StringBuilder();
int steps = random.nextInt(10) + 1;
for (int i = 0; i < steps; i++) {
if (sb.length() == 0 || random.nextBoolean()) { // Add literal
int len = random.nextInt(5) + 1;
for (int j = 0; j < len; j++)
sb.append(alphabet.charAt(random.nextInt(alphabet.length())));
} else { // Repeat prefix
int len = random.nextInt(sb.length()) + 1;
int reps = random.nextInt(3) + 1;
if (sb.length() + len * reps > 1000)
break;
for (int j = 0; j < reps; j++)
sb.append(sb.substring(0, len));
}
}
return sb.toString();
}
private static String randomText(String pattern) {
StringBuilder sb = new StringBuilder();
int steps = random.nextInt(100);
for (int i = 0; i < steps && sb.length() < 10000; i++) {
if (random.nextDouble() < 0.7) { // Add prefix of pattern
int len = random.nextInt(pattern.length()) + 1;
sb.append(pattern.substring(0, len));
} else { // Add literal
int len = random.nextInt(30) + 1;
for (int j = 0; j < len; j++)
sb.append(alphabet.charAt(random.nextInt(alphabet.length())));
}
}
return sb.toString();
}
private static void test(String pattern, String text) {
try {
assertEquals(text.indexOf(pattern), KMPSearch.indexOf(text.getBytes(), pattern.getBytes()));
} catch (AssertionError e) {
System.out.println("FAILED -> Unable to find '" + pattern + "' in '" + text + "'");
}
}
}
Java strings are composed of 16-bit chars, not of 8-bit bytes. A char can hold a byte, so you can always make your byte arrays into strings, and use indexOf: ASCII characters, control characters, and even zero characters will work fine.
Here is a demo:
byte[] big = new byte[] {1,2,3,0,4,5,6,7,0,8,9,0,0,1,2,3,4};
byte[] small = new byte[] {7,0,8,9,0,0,1};
String bigStr = new String(big, StandardCharsets.UTF_8);
String smallStr = new String(small, StandardCharsets.UTF_8);
System.out.println(bigStr.indexOf(smallStr));
This prints 7.
However, considering that your large array could be up to 10,000 bytes, and the small array is only ten bytes, this solution may not be the most efficient, for two reasons:
It requires copying your big array into an array that is twice as large (same capacity, but with char instead of byte). This triples your memory requirements.
String search algorithm of Java is not the fastest one available. You may get sufficiently faster if you implement one of the advanced algorithms, for example, the Knuth–Morris–Pratt one. This could potentially bring the execution speed down by a factor of up to ten (the length of the small string), and will require additional memory that is proportional to the length of the small string, not the big string.
For a little HTTP server I am currently working on, I came up with the following code to find boundaries in a multipart/form-data request. Hoped to find a better solution here, but i guess I'll stick with it. I think it is as efficent as it can get (quite fast and uses not much ram). It uses the input bytes as ring buffer, reads the next byte as soon as it does not match the boundary and writes the data after the first full cycle into the output stream. Of course can it be changed for byte arrays instead of streams, as asked in the question.
private boolean multipartUploadParseOutput(InputStream is, OutputStream os, String boundary)
{
try
{
String n = "--"+boundary;
byte[] bc = n.getBytes("UTF-8");
int s = bc.length;
byte[] b = new byte[s];
int p = 0;
long l = 0;
int c;
boolean r;
while ((c = is.read()) != -1)
{
b[p] = (byte) c;
l += 1;
p = (int) (l % s);
if (l>p)
{
r = true;
for (int i = 0; i < s; i++)
{
if (b[(p + i) % s] != bc[i])
{
r = false;
break;
}
}
if (r)
break;
os.write(b[p]);
}
}
os.flush();
return true;
} catch(IOException e) {e.printStackTrace();}
return false;
}

Most efficient way to search for unknown patterns in a string?

I am trying to find patterns that:
occur more than once
are more than 1 character long
are not substrings of any other known pattern
without knowing any of the patterns that might occur.
For example:
The string "the boy fell by the bell" would return 'ell', 'the b', 'y '.
The string "the boy fell by the bell, the boy fell by the bell" would return 'the boy fell by the bell'.
Using double for-loops, it can be brute forced very inefficiently:
ArrayList<String> patternsList = new ArrayList<>();
int length = string.length();
for (int i = 0; i < length; i++) {
int limit = (length - i) / 2;
for (int j = limit; j >= 1; j--) {
int candidateEndIndex = i + j;
String candidate = string.substring(i, candidateEndIndex);
if(candidate.length() <= 1) {
continue;
}
if (string.substring(candidateEndIndex).contains(candidate)) {
boolean notASubpattern = true;
for (String pattern : patternsList) {
if (pattern.contains(candidate)) {
notASubpattern = false;
break;
}
}
if (notASubpattern) {
patternsList.add(candidate);
}
}
}
}
However, this is incredibly slow when searching large strings with tons of patterns.
You can build a suffix tree for your string in linear time:
https://en.wikipedia.org/wiki/Suffix_tree
The patterns you are looking for are the strings corresponding to internal nodes that have only leaf children.
You could use n-grams to find patterns in a string. It would take O(n) time to scan the string for n-grams. When you find a substring by using a n-gram, put it into a hash table with a count of how many times that substring was found in the string. When you're done searching for n-grams in the string, search the hash table for counts greater than 1 to find recurring patterns in the string.
For example, in the string "the boy fell by the bell, the boy fell by the bell" using a 6-gram will find the substring "the boy fell by the bell". A hash table entry with that substring will have a count of 2 because it occurred twice in the string. Varying the number of words in the n-gram will help you discover different patterns in the string.
Dictionary<string, int>dict = new Dictionary<string, int>();
int count = 0;
int ngramcount = 6;
string substring = "";
// Add entries to the hash table
while (count < str.length) {
// copy the words into the substring
int i = 0;
substring = "";
while (ngramcount > 0 && count < str.length) {
substring[i] = str[count];
if (str[i] == ' ')
ngramcount--;
i++;
count++;
}
ngramcount = 6;
substring.Trim(); // get rid of the last blank in the substring
// Update the dictionary (hash table) with the substring
if (dict.Contains(substring)) { // substring is already in hash table so increment the count
int hashCount = dict[substring];
hashCount++;
dict[substring] = hashCount;
}
else
dict[substring] = 1;
}
// Find the most commonly occurrring pattern in the string
// by searching the hash table for the greatest count.
int maxCount = 0;
string mostCommonPattern = "";
foreach (KeyValuePair<string, int> pair in dict) {
if (pair.Value > maxCount) {
maxCount = pair.Value;
mostCommonPattern = pair.Key;
}
}
I've written this just for fun. I hope I have understood the problem correctly, this is valid and fast enough; if not, please be easy on me :) I might optimize it a little more I guess, if someone finds it useful.
private static IEnumerable<string> getPatterns(string txt)
{
char[] arr = txt.ToArray();
BitArray ba = new BitArray(arr.Length);
for (int shingle = getMaxShingleSize(arr); shingle >= 2; shingle--)
{
char[] arr1 = new char[shingle];
int[] indexes = new int[shingle];
HashSet<int> hs = new HashSet<int>();
Dictionary<int, int[]> dic = new Dictionary<int, int[]>();
for (int i = 0, count = arr.Length - shingle; i <= count; i++)
{
for (int j = 0; j < shingle; j++)
{
int index = i + j;
arr1[j] = arr[index];
indexes[j] = index;
}
int h = getHashCode(arr1);
if (hs.Add(h))
{
int[] indexes1 = new int[indexes.Length];
Buffer.BlockCopy(indexes, 0, indexes1, 0, indexes.Length * sizeof(int));
dic.Add(h, indexes1);
}
else
{
bool exists = false;
foreach (int index in indexes)
if (ba.Get(index))
{
exists = true;
break;
}
if (!exists)
{
int[] indexes1 = dic[h];
if (indexes1 != null)
foreach (int index in indexes1)
if (ba.Get(index))
{
exists = true;
break;
}
}
if (!exists)
{
foreach (int index in indexes)
ba.Set(index, true);
int[] indexes1 = dic[h];
if (indexes1 != null)
foreach (int index in indexes1)
ba.Set(index, true);
dic[h] = null;
yield return new string(arr1);
}
}
}
}
}
private static int getMaxShingleSize(char[] arr)
{
for (int shingle = 2; shingle <= arr.Length / 2 + 1; shingle++)
{
char[] arr1 = new char[shingle];
HashSet<int> hs = new HashSet<int>();
bool noPattern = true;
for (int i = 0, count = arr.Length - shingle; i <= count; i++)
{
for (int j = 0; j < shingle; j++)
arr1[j] = arr[i + j];
int h = getHashCode(arr1);
if (!hs.Add(h))
{
noPattern = false;
break;
}
}
if (noPattern)
return shingle - 1;
}
return -1;
}
private static int getHashCode(char[] arr)
{
unchecked
{
int hash = (int)2166136261;
foreach (char c in arr)
hash = (hash * 16777619) ^ c.GetHashCode();
return hash;
}
}
Edit
My previous code has serious problems. This one is better:
private static IEnumerable<string> getPatterns(string txt)
{
Dictionary<int, int> dicIndexSize = new Dictionary<int, int>();
for (int shingle = 2, count0 = txt.Length / 2 + 1; shingle <= count0; shingle++)
{
Dictionary<string, int> dic = new Dictionary<string, int>();
bool patternExists = false;
for (int i = 0, count = txt.Length - shingle; i <= count; i++)
{
string sub = txt.Substring(i, shingle);
if (!dic.ContainsKey(sub))
dic.Add(sub, i);
else
{
patternExists = true;
int index0 = dic[sub];
if (index0 >= 0)
{
dicIndexSize[index0] = shingle;
dic[sub] = -1;
}
}
}
if (!patternExists)
break;
}
List<int> lst = dicIndexSize.Keys.ToList();
lst.Sort((a, b) => dicIndexSize[b].CompareTo(dicIndexSize[a]));
BitArray ba = new BitArray(txt.Length);
foreach (int i in lst)
{
bool ok = true;
int len = dicIndexSize[i];
for (int j = i, max = i + len; j < max; j++)
{
if (ok) ok = !ba.Get(j);
ba.Set(j, true);
}
if (ok)
yield return txt.Substring(i, len);
}
}
Text in this book took 3.4sec in my computer.
Suffix arrays are the right idea, but there's a non-trivial piece missing, namely, identifying what are known in the literature as "supermaximal repeats". Here's a GitHub repo with working code: https://github.com/eisenstatdavid/commonsub . Suffix array construction uses the SAIS library, vendored in as a submodule. The supermaximal repeats are found using a corrected version of the pseudocode from findsmaxr in Efficient repeat finding via suffix arrays
(Becher–Deymonnaz–Heiber).
static void FindRepeatedStrings(void) {
// findsmaxr from https://arxiv.org/pdf/1304.0528.pdf
printf("[");
bool needComma = false;
int up = -1;
for (int i = 1; i < Len; i++) {
if (LongCommPre[i - 1] < LongCommPre[i]) {
up = i;
continue;
}
if (LongCommPre[i - 1] == LongCommPre[i] || up < 0) continue;
for (int k = up - 1; k < i; k++) {
if (SufArr[k] == 0) continue;
unsigned char c = Buf[SufArr[k] - 1];
if (Set[c] == i) goto skip;
Set[c] = i;
}
if (needComma) {
printf("\n,");
}
printf("\"");
for (int j = 0; j < LongCommPre[up]; j++) {
unsigned char c = Buf[SufArr[up] + j];
if (iscntrl(c)) {
printf("\\u%.4x", c);
} else if (c == '\"' || c == '\\') {
printf("\\%c", c);
} else {
printf("%c", c);
}
}
printf("\"");
needComma = true;
skip:
up = -1;
}
printf("\n]\n");
}
Here's a sample output on the text of the first paragraph:
Davids-MBP:commonsub eisen$ ./repsub input
["\u000a"
," S"
," as "
," co"
," ide"
," in "
," li"
," n"
," p"
," the "
," us"
," ve"
," w"
,"\""
,"–"
,"("
,")"
,". "
,"0"
,"He"
,"Suffix array"
,"`"
,"a su"
,"at "
,"code"
,"com"
,"ct"
,"do"
,"e f"
,"ec"
,"ed "
,"ei"
,"ent"
,"ere's a "
,"find"
,"her"
,"https://"
,"ib"
,"ie"
,"ing "
,"ion "
,"is"
,"ith"
,"iv"
,"k"
,"mon"
,"na"
,"no"
,"nst"
,"ons"
,"or"
,"pdf"
,"ri"
,"s are "
,"se"
,"sing"
,"sub"
,"supermaximal repeats"
,"te"
,"ti"
,"tr"
,"ub "
,"uffix arrays"
,"via"
,"y, "
]
I would use Knuth–Morris–Pratt algorithm (linear time complexity O(n)) to find substrings. I would try to find the largest substring pattern, remove it from the input string and try to find the second largest and so on. I would do something like this:
string pattern = input.substring(0,lenght/2);
string toMatchString = input.substring(pattern.length, input.lenght - 1);
List<string> matches = new List<string>();
while(pattern.lenght > 0)
{
int index = KMP(pattern, toMatchString);
if(index > 0)
{
matches.Add(pattern);
// remove the matched pattern occurences from the input string
// I would do something like this:
// 0 to pattern.lenght gets removed
// check for all occurences of pattern in toMatchString and remove them
// get the remaing shrinked input, reassign values for pattern & toMatchString
// keep looking for the next largest substring
}
else
{
pattern = input.substring(0, pattern.lenght - 1);
toMatchString = input.substring(pattern.length, input.lenght - 1);
}
}
Where KMP implements Knuth–Morris–Pratt algorithm. You can find the Java implementations of it at Github or Princeton or write it yourself.
PS: I don't code in Java and it is quick try to my first bounty about to close soon. So please don't give me the stick if I missed something trivial or made a +/-1 error.

Manually converting a string to an integer in Java

I'm having string consisting of a sequence of digits (e.g. "1234"). How to return the String as an int without using Java's library functions like Integer.parseInt?
public class StringToInteger {
public static void main(String [] args){
int i = myStringToInteger("123");
System.out.println("String decoded to number " + i);
}
public int myStringToInteger(String str){
/* ... */
}
}
And what is wrong with this?
int i = Integer.parseInt(str);
EDIT :
If you really need to do the conversion by hand, try this:
public static int myStringToInteger(String str) {
int answer = 0, factor = 1;
for (int i = str.length()-1; i >= 0; i--) {
answer += (str.charAt(i) - '0') * factor;
factor *= 10;
}
return answer;
}
The above will work fine for positive integers, if the number is negative you'll have to do a little checking first, but I'll leave that as an exercise for the reader.
If the standard libraries are disallowed, there are many approaches to solving this problem. One way to think about this is as a recursive function:
If n is less than 10, just convert it to the one-character string holding its digit. For example, 3 becomes "3".
If n is greater than 10, then use division and modulus to get the last digit of n and the number formed by excluding the last digit. Recursively get a string for the first digits, then append the appropriate character for the last digit. For example, if n is 137, you'd recursively compute "13" and tack on "7" to get "137".
You will need logic to special-case 0 and negative numbers, but otherwise this can be done fairly simply.
Since I suspect that this may be homework (and know for a fact that at some schools it is), I'll leave the actual conversion as an exercise to the reader. :-)
Hope this helps!
Use long instead of int in this case.
You need to check for overflows.
public static int StringtoNumber(String s) throws Exception{
if (s == null || s.length() == 0)
return 0;
while(s.charAt(0) == ' '){
s = s.substring(1);
}
boolean isNegative = s.charAt(0) == '-';
if (s.charAt(0) == '-' || (s.charAt(0) == '+')){
s = s.substring(1);
}
long result = 0l;
for (int i = 0; i < s.length(); i++){
int value = s.charAt(i) - '0';
if (value >= 0 && value <= 9){
if (!isNegative && 10 * result + value > Integer.MAX_VALUE ){
throw new Exception();
}else if (isNegative && -1 * 10 * result - value < Integer.MIN_VALUE){
throw new Exception();
}
result = 10 * result + value;
}else if (s.charAt(i) != ' '){
return (int)result;
}
}
return isNegative ? -1 * (int)result : (int)result;
}
Alternate approach to the answer already posted here. You can traverse the string from the front and build the number
public static void stringtoint(String s){
boolean isNegative=false;
int number =0;
if (s.charAt(0)=='-') {
isNegative=true;
}else{
number = number* 10 + s.charAt(0)-'0';
}
for (int i = 1; i < s.length(); i++) {
number = number*10 + s.charAt(i)-'0';
}
if(isNegative){
number = 0-number;
}
System.out.println(number);
}
Given the right hint, I think most people with a high school education can solve this own their own. Every one knows 134 = 100x1 + 10x3 + 1x4
The key part most people miss, is that if you do something like this in Java
System.out.println('0'*1);//48
it will pick the decimal representation of character 0 in ascii chart and multiply it by 1.
In ascii table character 0 has a decimal representation of 48. So the above line will print 48. So if you do something like '1'-'0' That is same as 49-48. Since in ascii chart, characters 0-9 are continuous, so you can take any char from 0 to 9 and subtract 0 to get its integer value. Once you have the integer value for a character, then converting the whole string to int is straight forward.
Here is another one solution to the problem
String a = "-12512";
char[] chars = a.toCharArray();
boolean isNegative = (chars[0] == '-');
if (isNegative) {
chars[0] = '0';
}
int multiplier = 1;
int total = 0;
for (int i = chars.length - 1; i >= 0; i--) {
total = total + ((chars[i] - '0') * multiplier);
multiplier = multiplier * 10;
}
if (isNegative) {
total = total * -1;
}
Use this:
static int parseInt(String str) {
char[] ch = str.trim().toCharArray();
int len = ch.length;
int value = 0;
for (int i=0, j=(len-1); i<len; i++,j--) {
int c = ch[i];
if (c < 48 || c > 57) {
throw new NumberFormatException("Not a number: "+str);
}
int n = c - 48;
n *= Math.pow(10, j);
value += n;
}
return value;
}
And by the way, you can handle the special case of negative integers, otherwise it will throw exception NumberFormatException.
You can do like this: from the string, create an array of characters for each element, keep the index saved, and multiply its ASCII value by the power of the actual reverse index. Sum the partial factors and you get it.
There is only a small cast to use Math.pow (since it returns a double), but you can avoid it by creating your own power function.
public static int StringToInt(String str){
int res = 0;
char [] chars = str.toCharArray();
System.out.println(str.length());
for (int i = str.length()-1, j=0; i>=0; i--, j++){
int temp = chars[j]-48;
int power = (int) Math.pow(10, i);
res += temp*power;
System.out.println(res);
}
return res;
}
Using Java 8 you can do the following:
public static int convert(String strNum)
{
int result =strNum.chars().reduce(0, (a, b)->10*a +b-'0');
}
Convert srtNum to char
for each char (represented as 'b') -> 'b' -'0' will give the relative number
sum all in a (initial value is 0)
(each time we perform an opertaion on a char do -> a=a*10
Make use of the fact that Java uses char and int in the same way. Basically, do char - '0' to get the int value of the char.
public class StringToInteger {
public static void main(String[] args) {
int i = myStringToInteger("123");
System.out.println("String decoded to number " + i);
}
public static int myStringToInteger(String str) {
int sum = 0;
char[] array = str.toCharArray();
int j = 0;
for(int i = str.length() - 1 ; i >= 0 ; i--){
sum += Math.pow(10, j)*(array[i]-'0');
j++;
}
return sum;
}
}
public int myStringToInteger(String str) throws NumberFormatException
{
int decimalRadix = 10; //10 is the radix of the decimal system
if (str == null) {
throw new NumberFormatException("null");
}
int finalResult = 0;
boolean isNegative = false;
int index = 0, strLength = str.length();
if (strLength > 0) {
if (str.charAt(0) == '-') {
isNegative = true;
index++;
}
while (index < strLength) {
if((Character.digit(str.charAt(index), decimalRadix)) != -1){
finalResult *= decimalRadix;
finalResult += (str.charAt(index) - '0');
} else throw new NumberFormatException("for input string " + str);
index++;
}
} else {
throw new NumberFormatException("Empty numeric string");
}
if(isNegative){
if(index > 1)
return -finalResult;
else
throw new NumberFormatException("Only got -");
}
return finalResult;
}
Outcome:
1) For the input "34567" the final result would be: 34567
2) For the input "-4567" the final result would be: -4567
3) For the input "-" the final result would be: java.lang.NumberFormatException: Only got -
4) For the input "12ab45" the final result would be: java.lang.NumberFormatException: for input string 12ab45
public static int convertToInt(String input){
char[] ch=input.toCharArray();
int result=0;
for(char c : ch){
result=(result*10)+((int)c-(int)'0');
}
return result;
}
Maybe this way will be a little bit faster:
public static int convertStringToInt(String num) {
int result = 0;
for (char c: num.toCharArray()) {
c -= 48;
if (c <= 9) {
result = (result << 3) + (result << 1) + c;
} else return -1;
}
return result;
}
This is the Complete program with all conditions positive, negative without using library
import java.util.Scanner;
public class StringToInt {
public static void main(String args[]) {
String inputString;
Scanner s = new Scanner(System.in);
inputString = s.nextLine();
if (!inputString.matches("([+-]?([0-9]*[.])?[0-9]+)")) {
System.out.println("error!!!");
} else {
Double result2 = getNumber(inputString);
System.out.println("result = " + result2);
}
}
public static Double getNumber(String number) {
Double result = 0.0;
Double beforeDecimal = 0.0;
Double afterDecimal = 0.0;
Double afterDecimalCount = 0.0;
int signBit = 1;
boolean flag = false;
int count = number.length();
if (number.charAt(0) == '-') {
signBit = -1;
flag = true;
} else if (number.charAt(0) == '+') {
flag = true;
}
for (int i = 0; i < count; i++) {
if (flag && i == 0) {
continue;
}
if (afterDecimalCount == 0.0) {
if (number.charAt(i) - '.' == 0) {
afterDecimalCount++;
} else {
beforeDecimal = beforeDecimal * 10 + (number.charAt(i) - '0');
}
} else {
afterDecimal = afterDecimal * 10 + number.charAt(i) - ('0');
afterDecimalCount = afterDecimalCount * 10;
}
}
if (afterDecimalCount != 0.0) {
afterDecimal = afterDecimal / afterDecimalCount;
result = beforeDecimal + afterDecimal;
} else {
result = beforeDecimal;
}
return result * signBit;
}
}
Works for Positive and Negative String Using TDD
//Solution
public int convert(String string) {
int number = 0;
boolean isNegative = false;
int i = 0;
if (string.charAt(0) == '-') {
isNegative = true;
i++;
}
for (int j = i; j < string.length(); j++) {
int value = string.charAt(j) - '0';
number *= 10;
number += value;
}
if (isNegative) {
number = -number;
}
return number;
}
//Testcases
public class StringtoIntTest {
private StringtoInt stringtoInt;
#Before
public void setUp() throws Exception {
stringtoInt = new StringtoInt();
}
#Test
public void testStringtoInt() {
int excepted = stringtoInt.convert("123456");
assertEquals(123456,excepted);
}
#Test
public void testStringtoIntWithNegative() {
int excepted = stringtoInt.convert("-123456");
assertEquals(-123456,excepted);
}
}
//Take one positive or negative number
String str="-90997865";
//Conver String into Character Array
char arr[]=str.toCharArray();
int no=0,asci=0,res=0;
for(int i=0;i<arr.length;i++)
{
//If First Character == negative then skip iteration and i++
if(arr[i]=='-' && i==0)
{
i++;
}
asci=(int)arr[i]; //Find Ascii value of each Character
no=asci-48; //Now Substract the Ascii value of 0 i.e 48 from asci
res=res*10+no; //Conversion for final number
}
//If first Character is negative then result also negative
if(arr[0]=='-')
{
res=-res;
}
System.out.println(res);
public class ConvertInteger {
public static int convertToInt(String numString){
int answer = 0, factor = 1;
for (int i = numString.length()-1; i >= 0; i--) {
answer += (numString.charAt(i) - '0') *factor;
factor *=10;
}
return answer;
}
public static void main(String[] args) {
System.out.println(convertToInt("789"));
}
}

A better algorithm to find the next palindrome of a number string

Firstly here is the problem:
A positive integer is called a palindrome if its representation in the decimal system is the same when read from left to right and from right to left. For a given positive integer K of not more than 1000000 digits, write the value of the smallest palindrome larger than K to output. Numbers are always displayed without leading zeros.
Input: The first line contains integer t, the number of test cases. Integers K are given in the next t lines.
Output: For each K, output the smallest palindrome larger than K.
Example
Input:
2
808
2133
Output:
818
2222
Secondly here is my code:
// I know it is bad practice to not cater for erroneous input,
// however for the purpose of the execise it is omitted
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Scanner;
import java.lang.Exception;
import java.math.BigInteger;
public class Main
{
public static void main(String [] args){
try{
Main instance = new Main(); // create an instance to access non-static
// variables
// Use java.util.Scanner to scan the get the input and initialise the
// variable
Scanner sc=null;
BufferedReader r = new BufferedReader(new InputStreamReader(System.in));
String input = "";
int numberOfTests = 0;
String k; // declare any other variables here
if((input = r.readLine()) != null){
sc = new Scanner(input);
numberOfTests = sc.nextInt();
}
for (int i = 0; i < numberOfTests; i++){
if((input = r.readLine()) != null){
sc = new Scanner(input);
k=sc.next(); // initialise the remainder of the variables sc.next()
instance.palindrome(k);
} //if
}// for
}// try
catch (Exception e)
{
e.printStackTrace();
}
}// main
public void palindrome(String number){
StringBuffer theNumber = new StringBuffer(number);
int length = theNumber.length();
int left, right, leftPos, rightPos;
// if incresing a value to more than 9 the value to left (offset) need incrementing
int offset, offsetPos;
boolean offsetUpdated;
// To update the string with new values
String insert;
boolean hasAltered = false;
for(int i = 0; i < length/2; i++){
leftPos = i;
rightPos = (length-1) - i;
offsetPos = rightPos -1; offsetUpdated = false;
// set values at opposite indices and offset
left = Integer.parseInt(String.valueOf(theNumber.charAt(leftPos)));
right = Integer.parseInt(String.valueOf(theNumber.charAt(rightPos)));
offset = Integer.parseInt(String.valueOf(theNumber.charAt(offsetPos)));
if(left != right){
// if r > l then offest needs updating
if(right > left){
// update and replace
right = left;
insert = Integer.toString(right);
theNumber.replace(rightPos, rightPos + 1, insert);
offset++; if (offset == 10) offset = 0;
insert = Integer.toString(offset);
theNumber.replace(offsetPos, offsetPos + 1, insert);
offsetUpdated = true;
// then we need to update the value to left again
while (offset == 0 && offsetUpdated){
offsetPos--;
offset =
Integer.parseInt(String.valueOf(theNumber.charAt(offsetPos)));
offset++; if (offset == 10) offset = 0;
// replace
insert = Integer.toString(offset);
theNumber.replace(offsetPos, offsetPos + 1, insert);
}
// finally incase right and offset are the two middle values
left = Integer.parseInt(String.valueOf(theNumber.charAt(leftPos)));
if (right != left){
right = left;
insert = Integer.toString(right);
theNumber.replace(rightPos, rightPos + 1, insert);
}
}// if r > l
else
// update and replace
right = left;
insert = Integer.toString(right);
theNumber.replace(rightPos, rightPos + 1, insert);
}// if l != r
}// for i
System.out.println(theNumber.toString());
}// palindrome
}
Finally my explaination and question.
My code compares either end and then moves in
if left and right are not equal
if right is greater than left
(increasing right past 9 should increase the digit
to its left i.e 09 ---- > 10) and continue to do
so if require as for 89999, increasing the right
most 9 makes the value 90000
before updating my string we check that the right
and left are equal, because in the middle e.g 78849887
we set the 9 --> 4 and increase 4 --> 5, so we must cater for this.
The problem is from spoj.pl an online judge system. My code works for all the test can provide but when I submit it, I get a time limit exceeded error and my answer is not accepted.
Does anyone have any suggestions as to how I can improve my algorithm. While writing this question i thought that instead of my while (offset == 0 && offsetUpdated) loop i could use a boolean to to make sure i increment the offset on my next [i] iteration. Confirmation of my chang or any suggestion would be appreciated, also let me know if i need to make my question clearer.
This seems like a lot of code. Have you tried a very naive approach yet? Checking whether something is a palindrome is actually very simple.
private boolean isPalindrome(int possiblePalindrome) {
String stringRepresentation = String.valueOf(possiblePalindrome);
if ( stringRepresentation.equals(stringRepresentation.reverse()) ) {
return true;
}
}
Now that might not be the most performant code, but it gives you a really simple starting point:
private int nextLargestPalindrome(int fromNumber) {
for ( int i = fromNumber + 1; ; i++ ) {
if ( isPalindrome( i ) ) {
return i;
}
}
}
Now if that isn't fast enough you can use it as a reference implementation and work on decreasing the algorithmic complexity.
There should actually be a constant-time (well it is linear on the number of digits of the input) way to find the next largest palindrome. I will give an algorithm that assumes the number is an even number of digits long (but can be extended to an odd number of digits).
Find the decimal representation of the input number ("2133").
Split it into the left half and right half ("21", "33");
Compare the last digit in the left half and the first digit in the right half.
a. If the right is greater than the left, increment the left and stop. ("22")
b. If the right is less than the left, stop.
c. If the right is equal to the left, repeat step 3 with the second-last digit in the left and the second digit in the right (and so on).
Take the left half and append the left half reversed. That's your next largest palindrome. ("2222")
Applied to a more complicated number:
1. 1234567887654322
2. 12345678 87654322
3. 12345678 87654322
^ ^ equal
3. 12345678 87654322
^ ^ equal
3. 12345678 87654322
^ ^ equal
3. 12345678 87654322
^ ^ equal
3. 12345678 87654322
^ ^ equal
3. 12345678 87654322
^ ^ equal
3. 12345678 87654322
^ ^ equal
3. 12345678 87654322
^ ^ greater than, so increment the left
3. 12345679
4. 1234567997654321 answer
This seems a bit similar to the algorithm you described, but it starts at the inner digits and moves to the outer.
There is no reason to fiddle with individual digits when the only needed operation is one simple addition. The following code is based on Raks' answer.
The code stresses simplicity over execution speed, intentionally.
import static org.junit.Assert.assertEquals;
import java.math.BigInteger;
import org.junit.Test;
public class NextPalindromeTest {
public static String nextPalindrome(String num) {
int len = num.length();
String left = num.substring(0, len / 2);
String middle = num.substring(len / 2, len - len / 2);
String right = num.substring(len - len / 2);
if (right.compareTo(reverse(left)) < 0)
return left + middle + reverse(left);
String next = new BigInteger(left + middle).add(BigInteger.ONE).toString();
return next.substring(0, left.length() + middle.length())
+ reverse(next).substring(middle.length());
}
private static String reverse(String s) {
return new StringBuilder(s).reverse().toString();
}
#Test
public void testNextPalindrome() {
assertEquals("5", nextPalindrome("4"));
assertEquals("11", nextPalindrome("9"));
assertEquals("22", nextPalindrome("15"));
assertEquals("101", nextPalindrome("99"));
assertEquals("151", nextPalindrome("149"));
assertEquals("123454321", nextPalindrome("123450000"));
assertEquals("123464321", nextPalindrome("123454322"));
}
}
Well I have constant order solution(atleast of order k, where k is number of digits in the number)
Lets take some examples
suppose n=17208
divide the number into two parts from middle
and reversibly write the most significant part onto the less significant one.
ie, 17271
if the so generated number is greater than your n it is your palindrome, if not just increase the center number(pivot) ie, you get 17371
other examples
n=17286
palidrome-attempt=17271(since it is less than n increment the pivot, 2 in this case)
so palidrome=17371
n=5684
palidrome1=5665
palidrome=5775
n=458322
palindrome=458854
now suppose n = 1219901
palidrome1=1219121
incrementing the pivot makes my number smaller here
so increment the number adjacent pivot too
1220221
and this logic could be extended
public class NextPalindrome
{
int rev, temp;
int printNextPalindrome(int n)
{
int num = n;
for (int i = num+1; i >= num; i++)
{
temp = i;
rev = 0;
while (temp != 0)
{
int remainder = temp % 10;
rev = rev * 10 + remainder;
temp = temp / 10;
}
if (rev == i)
{
break;
}
}
return rev;
}
public static void main(String args[])
{
NextPalindrome np = new NextPalindrome();
int nxtpalin = np.printNextPalindrome(11);
System.out.println(nxtpalin);
}
}
Here is my code in java. Whole idea is from here.
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("Enter number of tests: ");
int t = sc.nextInt();
for (int i = 0; i < t; i++) {
System.out.println("Enter number: ");
String numberToProcess = sc.next(); // ne proveravam dal su brojevi
nextSmallestPalindrom(numberToProcess);
}
}
private static void nextSmallestPalindrom(String numberToProcess) {
int i, j;
int length = numberToProcess.length();
int[] numberAsIntArray = new int[length];
for (int k = 0; k < length; k++)
numberAsIntArray[k] = Integer.parseInt(String
.valueOf(numberToProcess.charAt(k)));
numberToProcess = null;
boolean all9 = true;
for (int k = 0; k < length; k++) {
if (numberAsIntArray[k] != 9) {
all9 = false;
break;
}
}
// case 1, sve 9ke
if (all9) {
whenAll9(length);
return;
}
int mid = length / 2;
if (length % 2 == 0) {
i = mid - 1;
j = mid;
} else {
i = mid - 1;
j = mid + 1;
}
while (i >= 0 && numberAsIntArray[i] == numberAsIntArray[j]) {
i--;
j++;
}
// case 2 already polindrom
if (i == -1) {
if (length % 2 == 0) {
i = mid - 1;
j = mid;
} else {
i = mid;
j = i;
}
addOneToMiddleWithCarry(numberAsIntArray, i, j, true);
} else {
// case 3 not polindrom
if (numberAsIntArray[i] > numberAsIntArray[j]) { // 3.1)
while (i >= 0) {
numberAsIntArray[j] = numberAsIntArray[i];
i--;
j++;
}
for (int k = 0; k < numberAsIntArray.length; k++)
System.out.print(numberAsIntArray[k]);
System.out.println();
} else { // 3.2 like case 2
if (length % 2 == 0) {
i = mid - 1;
j = mid;
} else {
i = mid;
j = i;
}
addOneToMiddleWithCarry(numberAsIntArray, i, j, false);
}
}
}
private static void whenAll9(int length) {
for (int i = 0; i <= length; i++) {
if (i == 0 || i == length)
System.out.print('1');
else
System.out.print('0');
}
}
private static void addOneToMiddleWithCarry(int[] numberAsIntArray, int i,
int j, boolean palindrom) {
numberAsIntArray[i]++;
numberAsIntArray[j] = numberAsIntArray[i];
while (numberAsIntArray[i] == 10) {
numberAsIntArray[i] = 0;
numberAsIntArray[j] = numberAsIntArray[i];
i--;
j++;
numberAsIntArray[i]++;
numberAsIntArray[j] = numberAsIntArray[i];
}
if (!palindrom)
while (i >= 0) {
numberAsIntArray[j] = numberAsIntArray[i];
i--;
j++;
}
for (int k = 0; k < numberAsIntArray.length; k++)
System.out.print(numberAsIntArray[k]);
System.out.println();
}
}
Try this
public static String genNextPalin(String base){
//check if it is 1 digit
if(base.length()==1){
if(Integer.parseInt(base)==9)
return "11";
else
return (Integer.parseInt(base)+1)+"";
}
boolean check = true;
//check if it is all 9s
for(char a: base.toCharArray()){
if(a!='9')
check = false;
}
if(check){
String num = "1";
for(int i=0; i<base.length()-1; i++)
num+="0";
num+="1";
return num;
}
boolean isBasePalin = isPalindrome(base);
int mid = base.length()/2;
if(isBasePalin){
//if base is palin and it is odd increase mid and return
if(base.length()%2==1){
BigInteger leftHalf = new BigInteger(base.substring(0,mid+1));
String newLeftHalf = leftHalf.add(BigInteger.ONE).toString();
String newPalin = genPalin2(newLeftHalf.substring(0,mid),newLeftHalf.charAt(mid));
return newPalin;
}
else{
BigInteger leftHalf = new BigInteger(base.substring(0,mid));
String newLeftHalf = leftHalf.add(BigInteger.ONE).toString();
String newPalin = genPalin(newLeftHalf.substring(0,mid));
return newPalin;
}
}
else{
if(base.length()%2==1){
BigInteger leftHalf = new BigInteger(base.substring(0,mid));
BigInteger rightHalf = new BigInteger(reverse(base.substring(mid+1,base.length())));
//check if leftHalf is greater than right half
if(leftHalf.compareTo(rightHalf)==1){
String newPalin = genPalin2(base.substring(0,mid),base.charAt(mid));
return newPalin;
}
else{
BigInteger leftHalfMid = new BigInteger(base.substring(0,mid+1));
String newLeftHalfMid = leftHalfMid.add(BigInteger.ONE).toString();
String newPalin = genPalin2(newLeftHalfMid.substring(0,mid),newLeftHalfMid.charAt(mid));
return newPalin;
}
}
else{
BigInteger leftHalf = new BigInteger(base.substring(0,mid));
BigInteger rightHalf = new BigInteger(reverse(base.substring(mid,base.length())));
//check if leftHalf is greater than right half
if(leftHalf.compareTo(rightHalf)==1){
return genPalin(base.substring(0,mid));
}
else{
BigInteger leftHalfMid = new BigInteger(base.substring(0,mid));
String newLeftHalfMid = leftHalfMid.add(BigInteger.ONE).toString();
return genPalin(newLeftHalfMid);
}
}
}
}
public static String genPalin(String base){
return base + new StringBuffer(base).reverse().toString();
}
public static String genPalin2(String base, char middle){
return base + middle +new StringBuffer(base).reverse().toString();
}
public static String reverse(String in){
return new StringBuffer(in).reverse().toString();
}
static boolean isPalindrome(String str) {
int n = str.length();
for( int i = 0; i < n/2; i++ )
if (str.charAt(i) != str.charAt(n-i-1))
return false;
return true;
}
HI Here is another simple algorithm using python,
def is_palindrome(n):
if len(n) <= 1:
return False
else:
m = len(n)/2
for i in range(m):
j = i + 1
if n[i] != n[-j]:
return False
return True
def next_palindrome(n):
if not n:
return False
else:
if is_palindrome(n) is True:
return n
else:
return next_palindrome(str(int(n)+1))
print next_palindrome('1000010')
I have written comments to clarify what each step is doing in this python code.
One thing that need to be taken into consideration is that input can be very large that we can not simply perform integer operations on it. So taking input as string and then manipulating it would be much easier.
tests = int(input())
results = []
for i in range(0, tests):
pal = input().strip()
palen = len(pal)
mid = int(palen/2)
if palen % 2 != 0:
if mid == 0: # if the number is of single digit e.g. next palindrome for 5 is 6
ipal = int(pal)
if ipal < 9:
results.append(int(pal) + 1)
else:
results.append(11) # for 9 next palindrome will be 11
else:
pal = list(pal)
pl = l = mid - 1
pr = r = mid + 1
flag = 'n' # represents left and right half of input string are same
while pl >= 0:
if pal[pl] > pal[pr]:
flag = 'r' # 123483489 in this case pal[pl] = 4 and pal[pr] = 3 so we just need to copy left half in right half
break # 123484321 will be the answer
elif pal[pl] < pal[pr]:
flag = 'm' # 123487489 in this case pal[pl] = 4 and pal[pr] = 9 so copying left half in right half will make number smaller
break # in this case we need to take left half increment by 1 and the copy in right half 123494321 will be the anwere
else:
pl = pl -1
pr = pr + 1
if flag == 'm' or flag == 'n': # increment left half by one and copy in right half
if pal[mid] != '9': # if mid element is < 9 the we can simply increment the mid number only and copy left in right half
pal[mid] = str(int(pal[mid]) + 1)
while r < palen:
pal[r] = pal[l]
r = r + 1
l = l - 1
results.append(''.join(pal))
else: # if mid element is 9 this will effect entire left half because of carry
pal[mid] = '0' # we need to take care of large inputs so we can not just directly add 1 in left half
pl = l
while pal[l] == '9':
pal[l] = '0'
l = l - 1
if l >= 0:
pal[l] = str(int(pal[l]) + 1)
while r < palen:
pal[r] = pal[pl]
r = r + 1
pl = pl - 1
if l < 0:
pal[0] = '1'
pal[palen - 1] = '01'
results.append(''.join(pal))
else:
while r < palen: # when flag is 'r'
pal[r] = pal[l]
r = r + 1
l = l - 1
results.append(''.join(pal))
else: # even length almost similar concept here with flags having similar significance as in case of odd length input
pal = list(pal)
pr = r = mid
pl = l = mid - 1
flag = 'n'
while pl >= 0:
if pal[pl] > pal[pr]:
flag = 'r'
break
elif pal[pl] < pal[pr]:
flag = 'm'
break
else:
pl = pl -1
pr = pr + 1
if flag == 'r':
while r < palen:
pal[r] = pal[l]
r = r + 1
l = l - 1
results.append(''.join(pal))
else:
if pal[l] != '9':
pal[l] = str(int(pal[l]) + 1)
while r < palen:
pal[r] = pal[l]
r = r + 1
l = l - 1
results.append(''.join(pal))
else:
pal[mid] = '0'
pl = l
while pal[l] == '9':
pal[l] = '0'
l = l - 1
if l >= 0:
pal[l] = str(int(pal[l]) + 1)
while r < palen:
pal[r] = pal[pl]
r = r + 1
pl = pl - 1
if l < 0:
pal[0] = '1'
pal[palen - 1] = '01'
results.append(''.join(pal))
for xx in results:
print(xx)
We can find next palindrome easily like below.
private void findNextPalindrom(int i) {
i++;
while (!checkPalindrom(i)) {
i++;
}
Log.e(TAG, "findNextPalindrom:next palindrom is===" + i);
}
private boolean checkPalindrom(int num) {
int temp = num;
int rev = 0;
while (num > 0) {
int rem = num % 10;
rev = rev * 10 + rem;
num = num / 10;
}
return temp == rev;
}
Simple codes and test output:
class NextPalin
{
public static void main( String[] args )
{
try {
int[] a = {2, 23, 88, 234, 432, 464, 7887, 7657, 34567, 99874, 7779222, 2569981, 3346990, 229999, 2299999 };
for( int i=0; i<a.length; i++)
{
int add = findNextPalin(a[i]);
System.out.println( a[i] + " + " + add + " = " + (a[i]+add) );
}
}
catch( Exception e ){}
}
static int findNextPalin( int a ) throws Exception
{
if( a < 0 ) throw new Exception();
if( a < 10 ) return a;
int count = 0, reverse = 0, temp = a;
while( temp > 0 ){
reverse = reverse*10 + temp%10;
count++;
temp /= 10;
}
//compare 'half' value
int halfcount = count/2;
int base = (int)Math.pow(10, halfcount );
int reverseHalfValue = reverse % base;
int currentHalfValue = a % base;
if( reverseHalfValue == currentHalfValue ) return 0;
if( reverseHalfValue > currentHalfValue ) return (reverseHalfValue - currentHalfValue);
if( (((a-currentHalfValue)/base)%10) == 9 ){
//cases like 12945 or 1995
int newValue = a-currentHalfValue + base*10;
int diff = findNextPalin(newValue);
return base*10 - currentHalfValue + diff;
}
else{
return (base - currentHalfValue + reverseHalfValue );
}
}
}
$ java NextPalin
2 + 2 = 4
23 + 9 = 32
88 + 0 = 88
234 + 8 = 242
432 + 2 = 434
464 + 0 = 464
7887 + 0 = 7887
7657 + 10 = 7667
34567 + 76 = 34643
99874 + 25 = 99899
7779222 + 555 = 7779777
2569981 + 9771 = 2579752
3346990 + 443 = 3347433
229999 + 9933 = 239932
2299999 + 9033 = 2309032

Categories

Resources