The algorithmic complexity of the String functions in Java - java

My question is thus:
Suppose you have String myString = "SOME_CHARACTERS_THAT_NEED_MODIFICATION"; that you would want to have look like String modifiedString = "Some Characters That Need Modification". The "pure String" way to do it (and the case-independent way) would (optimize this as necessary):
//obtaining the locations of all the occurrences of '_'
int activeIndex = 0;
ArrayList <Integer> indexList = new ArrayList<Integer>();
while (activeIndex != -1)
{
activeIndex = myString.indexOf('_', activeIndex + 1);
indexList.add(new Integer(activeIndex));
}
//replacing all '_' with ' '
String tempString = myString.replace('_', ' ');
//declaring empty modifiedString
String modifiedString;
//lowercasing all characters that are not first characters of a word (here, a word is defined as being terminated by '_' or newline
for (int x = 0; x < indexList.size(); x++)
{
modifiedString += tempString.substring(indexList.get(x), indexList.get(x)+1);
if (x != indexList.size() - 1)
//appending first uppercase character of word plus lowercased characters of the rest of the word
modifiedString += tempString.subString(indexList.get(x)+1,indexList.get(x+1)).toLowerCase();
else
//we are near the end of the String (as far as ' ' is concerned)
modifiedString += tempString.substring(index.get(x), tempString.length().toLowerCase());
}
//moving this modified String to modifiedString
modifiedString = tempString;
The other way I was proposing to do this would have been to dump myString into an array of characters, and then do array-based manipulation of all the characters. This would be easy in C++; a String is both an array of characters and an Object there! My question is, however, would both algorithms have the same complexity? //As a character array, I could probably do some arithmetic, assuming that the alphanumeric characters are numerically in the ASCII range (0 through 127). In fact, (int)uppercaseChar == (int)lowercaseChar - 32; for any of the uppercaseChar ranging from A-Z and any corresponding lowercaseChar ranging from a-z.
The char[] way to do would probably be something like (may need optimization)
//declaring necessary variables and containers
int activeIndex = 0;
ArrayList<Integer> indexList = new ArrayList<Integer>();
while (activeIndex != -1)
{
//finding all '_'
activeIndex = myString.indexOf('_', activeIndex + 1);
//pushing it to indexArray
indexArray.add(new Integer(activeIndex));
}
//dumping String to char[]
char[] charArray = myString.toCharArray();
for (int x = 0; x < indexArray.size(); x++)
{
//making every '_' a ' '
charArray[indexArray.get(x)] = ' ';
//lowercasing every capitalized character that isn't a first character in a word
}

would both algorithms have the same complexity?
No. If the input string holds n consecutive underscores, then
for (int x = 0; x < indexList.size(); x++)
modifiedString += tempString.substring(indexList.get(x), indexList.get(x)+1);
will append a single underscore n times. Since the old value of modifiedString has to be copied every time around the loop at linear time cost, the whole algorithm takes quadratic time.
By contrast, the "char[] approach" takes linear time.

I think, doing it through ASCII or unicode way would be better.
Iterate through the array, Except for the first character, keep replacing all characters to lower case(through arithmetic calculation that you talked about) until you find a character whose ascii value is same as '_'. Once you get this character, again except for the first character, replace everything else with lower case, until again you get '_'. This can be done in just one iteration.
Whereas string.replace all would itself take one iteration just to replace.And your code would still look lot cleaner.
Note :Assuming the input pattern to be exactly the same.

Extending #zerocool 's answer, I found out the code to the way to do this optimally. It is something like:
private char[] charArray = myString.toCharArray();
int indexOfUnderscore = -1;
for (int x = 0; x < charArray.length; x++)
{
if (charArray[x] == '_')
{
charArray[x] = ' ';
indexOfUnderscore = x;
}
else
{
if (x > indexOfUnderscore + 1)
{
charArray[x] = (char)((int)charArray[x] + 32);
}
}
}
The algorithmic complexity of the above code would be, ignoring String.toCharArray(), O(length). We could then just say something like private String modifiedString = new String(charArray); to get it back as a String. I feel the character-array way is syntactically simpler to understand than the built-in String functions.
#zerocool, I wish I would have seen your answer when I thought up this.

Related

Problem replacing char in char array with a digit

Given a string, I have to replace all vowels with their respective position in the array. However, my code returns some strange symbols instead of numbers. Where's the problem?
String s = "this is my string";
char p = 1;
char[] formatted = s.toCharArray();
for(int i = 0; i < formatted.length; i++) {
if(formatted[i] == 'a' ||formatted[i] == 'e' ||formatted[i] == 'i'
||formatted[i] == 'o' ||formatted[i] == 'u') {
formatted[i] = p;
}
p++;
}
s = String.valueOf(formatted);
System.out.println(s);
P.S: Numbers are bigger than 10
this is my s t r i n g
012345678910 11 12 13 14
The position of i in string is 14 but 14 is not a character; it's a numeric string. It means that you need to deal with strings instead of characters. Split s using "" as the delimiter, process the resulting array and finally join the array back into a string using "" as the delimiter.
class Main {
public static void main(String[] args) {
String s = "this is my string";
String[] formatted = s.split("");
for (int i = 0; i < formatted.length; i++) {
if (formatted[i].matches("(?i)[aeiou]")) {
formatted[i] = String.valueOf(i);
}
}
s = String.join("", formatted);
System.out.println(s);
}
}
Output:
th2s 5s my str14ng
The regex, (?i)[aeiou] specifies case-insensitive match for one of the vowels where (?i) specifies the case-insensitiveness. Test it here.
The character '1' has a different value from the number 1.
You can change
char p = 1;
to
char p = '1';
and I think that will give you what you want, as long you're not trying to insert more than 9 numbers in your string. Otherwise you'll need to cope with inserting extra digits, which you cannot do into a char-array, because it has a fixed length.
the root of the problem is already in the comments,
in java the types make a difference in memory size and its representation
int x = 1;
and
char y = '1'
are not holding the same value, this is because many numerical representations are related with ascii codes and the value you have to assing to y to get the number 1 printed is HEX 0x31 or DEC 49.
take a look at the ascci table

How to reverse all the words in a string, without changing symbol position?

i have to write a program which will reverse all words in a string, but all symbols should stay on previous position for example: "a1bcd efg!h" => "d1cba hgf!e". I wrote a simple program which can reverse all words/symbols, but I have no idea how to make it like in example
public void reverseWordInMyString(String str) {
String[] words = str.split(" ");
String reversedString = "";
for (int i = 0; i < words.length; i++) {
String word = words[i];
String reverseWord = "";
for (int j = word.length()-1; j >= 0; j--) {
reverseWord = reverseWord + word.charAt(j);
}
reversedString = reversedString + reverseWord + " ";
}
System.out.println(reversedString);
}
It's a good start. The question is just that tricky.
Your current approach uses a single 'accumulator' which starts at the end of the string and moves back to the start: The j in for (int j =...).
You'll need two accumulators to complete this homework assignment: One going from the front to the back, which steadily increments (so, that'll be for (int i = 0; i < word.length(); i++)), and one which starts at the end and decrements, but not steadily.
The idea is: As you go forward, you inspect the character you find at position i. Then, you use an if, as the question asks you to do different things depending on a condition:
if the character at i is a special character, just add it.
else, add the last non-special character in the string we haven't yet added.
the if case is trivial. The else case is not. That's where your second accumulator comes in: This one will track where you're at in the string, from the end. This is a loop-in-loop. What you'll need to:
repeat the following algorithm:
If the character at 'j' (which goes from end to start) is a special character, decrement j, and restart this algorithm
Otherwise, that's the 'last non-special character we havent yet added', so add that, decrement j, and escape this algorithm.
The above can be done with, for example, a while or do/while loop. It'll be inside your for loop.
Good luck!
NB: This isn't the only way to do it. For example, you could also eliminate all special characters from the input, do a basic reverse on every word inside, which is a lot simpler than what you have now, as string has a .reverse() method these days, and then, after all that, go through your original input character by character, and for each special character you find, insert that character at that position in your output string. That works too. Whichever strategy you prefer!
according to www.geeksforgeeks.org
The Problem:
Given a string, that contains special character together with alphabets (‘a’ to ‘z’ and ‘A’ to ‘Z’), reverse the string in a way that special characters are not affected.
Solution:
Create a temporary character array say temp[].
Copy alphabetic characters from given array to temp[].
Reverse temp[] using standard string reversal algorithm.
Now traverse input string and temp in a single loop. Wherever there is alphabetic character is input string, replace it with current character of temp[].
Algorithm:
1) Let input string be 'str[]' and length of string be 'n'
2) l = 0, r = n-1
3) While l is smaller than r, do following
a) If str[l] is not an alphabetic character, do l++
b) Else If str[r] is not an alphabetic character, do r--
c) Else swap str[l] and str[r]
Java Code:
public static void main(String[] args){
String s = "Thi!s is a sa5mpl?e sentence.";
String[] words = s.split("\\s+");
System.out.println("Result:" + reverse(s));
//Output: sih!T si a el5pma?s ecnetnes.
}
public static String reverse(String input)
{
String[] words = input.split("\\s+");
String last_str = "";
for(int j=0;j<words.length;j++){
char[] str = words[j].toCharArray();
int r = str.length - 1, l = 0;
// Traverse string from both ends until
// 'l' and 'r'
while (l < r)
{
// Ignore special characters
if (!Character.isAlphabetic(str[l]))
l++;
else if(!Character.isAlphabetic(str[r]))
r--;
// Both str[l] and str[r] are not spacial
else
{
str[l] ^= str[r];//swap using triple XOR
str[r] ^= str[l];
str[l] ^= str[r];
l++;
r--;
}
}
last_str = last_str + new String(str) + " ";
}
// Initialize left and right pointers
return last_str;
}
}
I would approach this as follows:
Keep track of where the current word starts (or, equivalently, where the last non-word character was). This will be updated as we go along.
Scan the input string. Every time a non-word character (special character or space) is found, output the reverse of the current word and then the non-word character. You can output the reverse of the current word by indexing backwards from just before the non-word character to the start of the current word. (Note that the current word might be empty; for example, two special characters in a row).
For the output area, I recommend a StringBuilder rather than a String; this will be more efficient.
In code, it might look something like this (where I've changed the method to return the result rather than print it to the console):
public String reverseWordInMyString(String str) {
StringBuilder output = new StringBuilder(str.length()); // initialize full capacity
int curWordStart = 0;
// scan the input string
for (int i = 0; i < str.length(); i++) {
char curLetter = str.charAt(i);
if (!Character.isLetter(char)) {
// the current word has ended--output it in reverse
for (int j = i-1; j >= curWordStart; j--) {
output.append(str.charAt(j));
}
// output the current letter
output.append(curLetter);
// the next current word starts just after the current letter
curWordStart = i + 1;
}
}
// The last current word (if any) ends with the end of string,
// not a special character, so add it (reversed) as well to output
for (int j = str.length() - 1; j >= curWordStart; j--) {
output.append(str.charAt(j));
}
return output.toString();
}

How to find matching characters in two strings using regex java

In a given two words, is it possible to use regex to find multiple strings matching character as well index.
For example:
String1 = cat
String2 = carrot
the first 2 characters and indexes are matching (ca). t does not count because it is not in the same index.
I've tried for loop. However it appears to be not working and not very efficient.
for (int i = 0; i < string1.length(); i++){
for (int j = 0; j < string2.length(); j++){
char ch1 = string1.charAt(i);
char ch2 = string2.charAt(j);
if (ch1 == ch2) {
count char++;
}
}
What you are probably looking for is the longest common prefix.
See Find longest common prefix?
Regex is for pattern matching. It is a solution to a different problem.
For loop can still work for this job to find positions where each string has same char and number of times this occurs:
ArrayList<Integer> places = new ArrayList<Integer>();
for (int i = 0; i < Math.min(string1.length(), string2.length()); i++) {
a = string1.charAt(i);
b = string2.charAt(i);
if (a == b) {
count++;
places.add(i); //To say at which indices the 2 strings have the same chars
}
}
I guess you want to count the number of characters repeated at the same positions in two words. (Not same prefix)
In words cat carrot, you want to get 2 since c and a are in the same position, but t is not.
in words carrot cabra, you will get 3, since c, a and r (4th) are the same in the same position.
You only need to iterate one time over the two strings at the same time:
String string1 = "car";
String string2 = "carrot";
int minLength = Math.min( string1.length(), string2.length() );
int count = 0;
for (int i = 0; i < minLength; i++){
char ch1 = string1.charAt(i);
char ch2 = string2.charAt(i);
if (ch1 == ch2) {
count++;
}
}
We use minLength since we only need to check until the length of the smallest word.
We use string1.charAt(i) and string2.charAt(i), with same index i, since we want to check characters in the same position.

Compressing a file

I want to accomplish a program which can take a textfile and make the size smaller. So far it replaces all the double character occurrences, and now I want to replace "ou" with "1".
I've tried with an if-statement, but it doesn't seem to work quite well.
My method is below:
public String compressIt (String input)
{
int length = input.length(); // length of input
int ix = 0; // actual index in input
char c; // actual read character
int cCounter; // occurrence counter of actual character
String ou = "ou";
StringBuilder output = // the output
new StringBuilder(length);
// loop over every character in input
while(ix < length)
{
// read character at actual index then increments the index
c = input.charAt(ix++);
// we count one occurrence of this character here
cCounter = 1;
// while not reached end of line and next character
// is the same as previously read
while(ix < length && input.charAt(ix) == c)
{
// inc index means skip this character
ix++;
// and inc character occurence counter
cCounter++;
}
if (input.charAt(ix) == 'o' && input.charAt(++ix) == 'u' && ix < length - 1)
{
output.append("1");
}
// if more than one character occurence is counted
if(cCounter > 1)
{
// print the character count
output.append(cCounter);
}
// print the actual character
output.append(c);
}
// return the full compressed output
return output.toString();
}
It's this lines of code I'm referring to.
if (input.charAt(ix) == 'o' && input.charAt(ix + 1) == 'u')
{
output.append("1");
}
What I want to do: Replace characters. I got a text-file which contains "Alice In Wonderland". When my looping through all characters sees an 'o' and an 'u' (like "You"), I want to replace the characters so it looks like: "Y1".
Regards
So most likely you're trying loop from ix = 0 to the length of the string.
First of all my guess is that youre looping up and including string.length(). Which doesnt work, charAt is 0 indexed aka
"abc" has a charAt 0, 1 and 2 but not 3 which gives the error you describe.
Second of all the line you showed uses input.charAt (ix++) which does the following: get the char at position ix (old value) and after that, update the value ix to ix + 1, if you want ix to be updated before the surrounding charAt you'd have to write input.charAt(++ix)
Third of all there is a String.replace function, input.replace("abc", "def") will work great for simple replacements, for more complicated replacements, consider using regex.
This has nothing to do with the charAt method. You need to change your if condition to run it till length-1. It is failing in the last case as it is going out array.
for(int i=0; i<inputString.length() - 1; i++)
{
char temp = inputString.charAt(i);
char blah = inputString.charAt(i+1);
System.out.println("temp: "+ temp);
System.out.println("blah: "+ blah);
}
This works for me!

I want to be able to convert numbers into text according to the ASCII decimal table

I am trying to make it so that I can take individual three-character substrings and convert them to integers under the conditions tht the length of the String is a multiple of three. The integers into which the partioned substrings are converted are supposed to function as relative positions in an array that contains all the printing characters of the ASCII table.
String IntMessage = result.toString();
if
{
(IntMessage.substring(0,1)=="1" && IntMessage.length()%3==0)
for(j=0;j < IntMessage.length()-2;j += 3)
n = Integer.parseInt(IntMessage.substring(j,j+3));
mess += ASCII[n-32];
return mess;
Under otherwise conditions, the method should take the first two characters of the String and initialize them to a variable i. In this case, the variable mess is initialized to the character in the ASCII array with an index of i-32. Then there is a for loop that takes the remaining characters and partitions them into three-digit substrings and they are taken and changed into strings according to their corresponding positions in the ASCII array. The String variables in this array are continuously added on to the the variable mess in order to get the BigInteger to String conversion of the IntMessage String.
int i = Integer.parseInt(IntMessage.substring(0,2));
mess=ASCII[i-32];
for(l=2; l< IntMessage.length() - 2; l+=3)
r = Integer.parseInt(IntMessage.substring(l,l+3));
mess+=ASCII[r-32];
return mess;
For some reason the method isn't working and I was wondering whether I was doing something wrong. I know how to take an input String and convert it into a series of numbers but I want to do the opposite also. Is there anyway you could help?
Based on your description you can use the following methods:
String fromIntMessage(String msg) {
StringBuilder result = new StringBuilder();
for (int x = (msg.length() % 3 - 3) % 3; x < msg.length(); x += 3) {
int chr = Integer.parseInt(msg.substring(Math.max(x, 0), x + 3));
result.append(Character.toString((char) (chr - 32)));
}
return result.toString();
}
String toIntMessage(String string) {
StringBuilder result = new StringBuilder();
for (char c : string.toCharArray()) {
result.append(String.format("%03d", c + 32));
}
return result.charAt(0) == '0' ? result.substring(1) : result.toString();
}
which will give you
toIntMessage("DAA") // => "100097097"
fromIntMessage("100097097") // => "DAA"

Categories