I wrote this code in C# to encrypt a string with a key:
private static int Bin2Dec(string num)
{
int _num = 0;
for (int i = 0; i < num.Length; i++)
_num += (int)Math.Pow(2, num.Length - i - 1) * int.Parse(num[i].ToString());
return _num;
}
private static string Dec2Bin(int num)
{
if (num < 2) return num.ToString();
return Dec2Bin(num / 2) + (num % 2).ToString();
}
public static string StrXor(string str, string key)
{
string _str = "";
string _key = "";
string _xorStr = "";
string _temp = "";
for (int i = 0; i < str.Length; i++)
{
_temp = Dec2Bin(str[i]);
for (int j = 0; j < 8 - _temp.Length + 1; j++)
_temp = '0' + _temp;
_str += _temp;
}
for (int i = 0; i < key.Length; i++)
{
_temp = Dec2Bin(key[i]);
for (int j = 0; j < 8 - _temp.Length + 1; j++)
_temp = '0' + _temp;
_key += _temp;
}
while (_key.Length < _str.Length) _key += _key;
if (_key.Length > _str.Length) _key = _key.Substring(0, _str.Length);
for (int i = 0; i < _str.Length; i++)
if (_str[i] == _key[i]) { _xorStr += '0'; } else { _xorStr += '1'; }
_str = "";
for (int i = 0; i < _xorStr.Length; i += 8)
{
char _chr = (char)0;
_chr = (char)Bin2Dec(_xorStr.Substring(i, 8)); //ERROR : (Index and length must refer to a location within the string. Parameter name: length)
_str += _chr;
}
return _str;
}
The problem is that I always get error when I want to decrypt an encryted text with this code:
string enc_text = ENCRYPT.XORENC("abc","a"); // enc_text = " ♥☻"
string dec_text = ENCRYPT.XORENC(enc_text,"a"); // ArgumentOutOfRangeException
Any clues?
If you have a character, a char, you can convert it to an integer, an int.
And then you can use the ^ operator to perform XOR on it. You don't appear to be using that operator at the moment, which might be the source of your problem.
string EncryptOrDecrypt(string text, string key)
{
var result = new StringBuilder();
for (int c = 0; c < text.Length; c++)
result.Append((char)((uint)text[c] ^ (uint)key[c % key.Length]));
return result.ToString();
}
That kind of thing. Here's a longer version with comments that does the same thing in steps, to make it easier to learn from:
string EncryptOrDecrypt(string text, string key)
{
var result = new StringBuilder();
for (int c = 0; c < text.Length; c++)
{
// take next character from string
char character = text[c];
// cast to a uint
uint charCode = (uint)character;
// figure out which character to take from the key
int keyPosition = c % key.Length; // use modulo to "wrap round"
// take the key character
char keyChar = key[keyPosition];
// cast it to a uint also
uint keyCode = (uint)keyChar;
// perform XOR on the two character codes
uint combinedCode = charCode ^ keyCode;
// cast back to a char
char combinedChar = (char)combinedCode;
// add to the result
result.Append(combineChar);
}
return result.ToString();
}
The short version is the same but with the intermediate variables removed, substituting expressions directly into where they're used.
// Code
public static byte[] EncryptOrDecrypt(byte[] text, byte[] key)
{
byte[] xor = new byte[text.Length];
for (int i = 0; i < text.Length; i++)
{
xor[i] = (byte)(text[i] ^ key[i % key.Length]);
}
return xor;
}
// Test
static void Main(string[] args){
string input;
byte[] inputBytes;
string inputKey;
byte[] key;
do
{
input = System.Console.ReadLine();
inputBytes = Encoding.Unicode.GetBytes(input);
inputKey = System.Console.ReadLine();
key = Encoding.Unicode.GetBytes(inputKey);
//byte[] key = { 0, 0 }; if key is 0, encryption will not happen
byte[] encryptedBytes = EncryptOrDecrypt(inputBytes, key);
string encryptedStr = Encoding.Unicode.GetString(encryptedBytes);
byte[] decryptedBytes = EncryptOrDecrypt(encryptedBytes, key);
string decryptedStr = Encoding.Unicode.GetString(decryptedBytes);
System.Console.WriteLine("Encrypted string:");
System.Console.WriteLine(encryptedStr);
System.Console.WriteLine("Decrypted string:");
System.Console.WriteLine(decryptedStr);
} while (input != "-1" && inputKey != "-1");
//test:
//pavle
//23
//Encrypted string:
//BRD_W
//Decrypted string:
//pavle
}
Here is some simple code to encrypt and decrypt
class CEncryption
{
public static string Encrypt(string strIn, string strKey)
{
string sbOut = String.Empty;
for (int i = 0; i < strIn.Length; i++)
{
sbOut += String.Format("{0:00}", strIn[i] ^ strKey[i % strKey.Length]);
}
return sbOut;
}
public static string Decrypt(string strIn, string strKey)
{
string sbOut = String.Empty;
for (int i = 0; i < strIn.Length; i += 2)
{
byte code = Convert.ToByte(strIn.Substring(i, 2));
sbOut += (char)(code ^ strKey[(i/2) % strKey.Length]);
}
return sbOut;
}
}
Related
G'Day. I am attempting to convert a function that was initially made for Bohemia Interactives language SQF to C#. The script is designed to encrypt a string with the encryption method RC4, however, it works a little bit different to the way this is traditionally done thanks to limitations of the arma3 engine. The whole idea of this is so I can encrypt a string with the C# program, and decrypt it through Arma 3 in-game.
Here is the code I am trying to convert from.
/*
Function: ALiVE_fnc_crypt
Author(s): Naught
Version: 1.0
Description:
Encrypts or decrypts a string with a specified encryption key.
Parameters:
0 - Decrypt (0) or encrypt (1) [number]
1 - Encryption method name [string]
2 - Encrypted or plain data [string]
3 - Encryption key [string]
Returns:
Encrypted or decrypted data or nothing on failure [string:nil]
Note(s):
1. Current encryption method names:
- "rc4" // Rivest Cipher 4 Stream Encryption Algorithm
*/
// Constants
MAX_CHAR_SIZE = 8;
CHAR_ZERO_REP = 256;
private ["_method", "_key"];
_encText = _this select 0;
_key = _this select 1;
private ["_fnc_intToBin"];
_fnc_intToBin = {
private ["_int", "_bin", "_pwr", "_bool"];
_int = _this select 0;
_bin = if ((count _this) > 1) then {_this select 1} else {[]};
for "_i" from (MAX_CHAR_SIZE - 1) to 0 step (-1) do
{
_pwr = 2^(_i);
_bool = _pwr <= _int;
_bin set [(count _bin), _bool];
if (_bool) then {_int = _int - _pwr};
};
_bin
};
private ["_bin"];
_bin = [];
// Convert string to UTF-8 binary
{ // count (faster than forEach)
[(if (_x == CHAR_ZERO_REP) then {0} else {_x}), _bin] call _fnc_intToBin;
false;
} count toArray(_encText);
systemChat str _bin;
// Encrypt & decrypt methods
_key = toArray(_key);
private ["_keyLen", "_state", "_temp", "_j"];
_keyLen = count _key;
_state = [];
_temp = 0;
_j = 0;
// Key-Scheduling Algorithm
for "_i" from 0 to 255 do {_state set [_i,_i]};
for "_i" from 0 to 255 do
{
_temp = _state select _i;
_j = (_j + _temp + (_key select (_i mod _keyLen))) mod 256;
_state set [_i, (_state select _j)];
_state set [_j, _temp];
};
private ["_temp1", "_temp2", "_rand", "_i", "_mod", "_rbit"];
_temp1 = 0;
_temp2 = 0;
_rand = [];
_i = 0;
_j = 0;
hint str _bin;
// Pseudo-Random Generation Algorithm
{
_mod = _forEachIndex % MAX_CHAR_SIZE;
if (_mod == 0) then
{
_i = (_i + 1) mod 256;
_j = (_j + (_state select _i)) mod 256;
_temp1 = _state select _i;
_temp2 = _state select _j;
_state set [_i, _temp2];
_state set [_j, _temp1];
_rand = [(_state select ((_temp1 + _temp2) mod 256))] call _fnc_intToBin;
};
_rbit = _rand select _mod;
_bin set [_forEachIndex, (_x && !_rbit) || {!_x && _rbit}]; // XOR
} forEach _bin;
private ["_dec", "_buf", "_mod"];
_dec = 0;
_buf = [];
// Convert binary array to UTF-8 string
{
_mod = _forEachIndex % MAX_CHAR_SIZE;
if (_x) then {_dec = _dec + 2^((MAX_CHAR_SIZE - 1) - _mod)};
if (_mod == 7) then
{
if (_dec == 0) then {_dec = CHAR_ZERO_REP};
_buf set [(count _buf), _dec];
_dec = 0;
};
} forEach _bin;
toString(_buf)
I have made slight adjustments to this code, and it all still works fine in Arma 3.
Now, the converted function. I have changed about everything in this code from what my initial code was to try and get it working, so at this point, it may be completely incorrect, however, I will include it anyway.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace encrypt
{
class Program
{
static public int MAX_CHAR_SIZE = 8;
static public int CHAR_ZERO_REP = 256;
static public int _keyLen;
static void Main()
{
string[] args = new string[2];
args[0] = "Some Random Code";
args[1] = "4tuIc9tai";
string text = args[0];
// Convert the text to an array of decimal unicode || Same as toArray in arma 3
string[] txtArray = new string[text.Length];
txtArray = text.ToCharArray().Select(c => c.ToString()).ToArray();
int[] utf = getUni(txtArray);
// Convert the key to an array of decimal unicode || Same as toArray in arma 3
string[] keyArray = new string[args[1].Length];
keyArray = args[1].ToCharArray().Select(c => c.ToString()).ToArray();
int[] key = getUni(keyArray);
// Debug the output
//foreach (int i in utf){Console.Write(i); Console.Write(",");} Console.WriteLine();foreach (int i in key){Console.Write(i);Console.Write(","); }
// Convert string to UTF-8 binary
Dictionary<int, bool> _bin = new Dictionary<int, bool>();
foreach (int i in utf)
{
int _num;
if (i == CHAR_ZERO_REP)
{
_num = 0;
}
else
{
_num = i;
}
_bin = intToBin(_num, _bin);
}
foreach (KeyValuePair<int, bool> i in _bin)
{
Console.Write(Convert.ToString(i.Value));
Console.Write(", ");
}
// Encrypt & decrypt methods
_keyLen = key.Length;
int[] _state = new int[1024];
int _temp = 0;
int _j = 0;
// Key-Scheduling Algorithm
for (int _i = 0; _i < 255; _i++)
{
_state.SetValue(_i, _i);
}
for (int _i = 0; _i < 255; _i++)
{
_temp = _state[_i];
_j = (_j + _temp + key[_i % _keyLen]) % 256;
_state.SetValue(_i, _state[_j]);
_state.SetValue(_j, _temp);
}
// Pseudo-Random Generation Algorithm
int _temp1 = 0;
int _temp2 = 0;
Dictionary<int, bool> _rand = new Dictionary<int, bool>();
int __i = 0;
int __j = 0;
int indx = 0;
for (int i = 0; i < _bin.Count-1; i++)
{
int _mod = indx % MAX_CHAR_SIZE;
if (_mod == 0)
{
__i = (__i + 1) % 256;
__j = (__j + _state[__i]) % 256;
_temp1 = _state[__i];
_temp2 = _state[__j];
_state.SetValue(__i, _temp2);
_state.SetValue(__j, _temp1);
_rand = intToBin(_state[_temp1 + _temp2] % 256, _rand);
}
bool _rbit = _rand[_mod];
bool _bit = _bin[i];
if (_bin.ContainsKey(indx))
{
_bin.Remove(indx);
}
_bin.Add(indx, (_bit && !_rbit) || (!_bit && _rbit));
indx++;
}
// Convert binary array to UTF-8 string
int _dec = 0;
int[] _buf = new int[_bin.Count];
for (int i = 0; i < _bin.Count-1; i++)
{
int _mod = i % MAX_CHAR_SIZE;
Console.WriteLine(i);
if (_bin[i])
{
_dec = _dec + 2 ^ ((MAX_CHAR_SIZE - 1) - _mod);
}
if (_mod == 7)
{
if (_dec == 0)
{
_dec = CHAR_ZERO_REP;
}
_buf.SetValue(_dec, _buf.Length-1);
_dec = 0;
}
_buf.SetValue(_dec, _buf.Length - 1);
}
Console.ReadLine();
}
static int[] getUni(string[] txtArray)
{
int[] ret = new int[txtArray.Length];
int idx = 0;
foreach (string i in txtArray)
{
ret.SetValue(Encoding.UTF8.GetBytes(i)[0], idx);
idx++;
}
return ret;
}
static Dictionary<int, bool> intToBin(int val, Dictionary<int, bool> bin)
{
for (int i = MAX_CHAR_SIZE-1; i > 0; i--)
{
int _pwr = 2 ^ i;
bool _bool = _pwr <= val;
int cc = bin.Count;
if (bin.ContainsKey(cc))
{
bin.Remove(cc);
}
bin.Add(cc, _bool);
if (_bool)
{
val = val - _pwr;
}
}
return bin;
}
}
}
Any help what so ever would be great :)
I'm trying to decrypt xor string with key in c# but decrypting is wrong and i'm getting wrong value.
string text = "xorhash";
string key = "xorkey";
var result = new StringBuilder();
for (int c = 0; c < text.Length; c++)
result.Append((char)((uint)text[c] ^ (uint)key[c % key.Length]));
return result.ToString();
I've get it from this python code, which working good.
def xor(message, key):
return "".join(chr(ord(message[i]) ^ ord(key[i % len(key)])) for i in xrange(len(message)))
key = "my_xor_key"
message = "my_xor_hash".decode("hex")
print xor(message, key)
as soon as your input string is actually hex representation of codes, c# code should look like this:
for (int c = 0; c < text.Length; c+=2)
result.Append((char)(Convert.ToUInt16(text.Substring(c, 2), 16) ^ (ushort)key[ (c/2) % key.Length]));
private static string xor(string text, string key) {
var result = new StringBuilder();
for (int c = 0; c < text.Length; c++)
result.Append((char)((uint)text[c] ^ (uint)key[c % key.Length]));
return result.ToString();
}
string text = "my_xor_hash";
string key = "my_xor_key";
string encrypt = xor(text, key);
string decrypt = xor(encrypt, key);
System.Console.Write("Encrypt " + encrypt);
System.Console.Write("Decrypt " + decrypt);
Prints:
Encrypt
Decrypt my_xor_hash
And I didn't change a single line, only indentation.
Edit:
private static string xor(string text, string key) {
var result = new StringBuilder();
for (int c = 0; c < text.Length; c++)
result.Append((char)((uint)text[c] ^ (uint)key[c % key.Length]));
return result.ToString();
}
private static string FromHex(string hex) {
byte[] raw = new byte[hex.Length / 2];
for (int i = 0; i < raw.Length; i++) {
raw[i] = Convert.ToByte(hex.Substring(i * 2, 2), 16);
}
return Encoding.ASCII.GetString(raw);
}
public static void Main() {
string text = FromHex("xor_hash");
string key = "xor_key";
string decrypt = xor(text, key);
System.Console.Write("Decrypt " + decrypt);
}
Prints:
Decrypt HARPERS
public static byte[] EncryptOrDecrypt(byte[] text, byte[] key)
{
byte[] xor = new byte[text.Length];
for (int i = 0; i < text.Length; i++)
{
xor[i] = (byte)(text[i] ^ key[i % key.Length]);
}
return xor;
}
static void Main(string[] args){
string input;
byte[] inputBytes;
string inputKey;
byte[] key;
do
{
input = System.Console.ReadLine();
inputBytes = Encoding.Unicode.GetBytes(input);
inputKey = System.Console.ReadLine();
key = Encoding.Unicode.GetBytes(inputKey);
//byte[] key = { 0, 0 }; if key is 0, encryption will not happen
byte[] encryptedBytes = EncryptOrDecrypt(inputBytes, key);
string encryptedStr = Encoding.Unicode.GetString(encryptedBytes);
byte[] decryptedBytes = EncryptOrDecrypt(encryptedBytes, key);
string decryptedStr = Encoding.Unicode.GetString(decryptedBytes);
System.Console.WriteLine("Encrypted string:");
System.Console.WriteLine(encryptedStr);
System.Console.WriteLine("Decrypted string:");
System.Console.WriteLine(decryptedStr);
} while (input != "-1" && inputKey != "-1");
//test:
//pavle
//23
//Encrypted string:
//BRD_W
//Decrypted string:
//pavle
}
I'm attempting to subtract two strings (of theoretically infinite length) without the use of libraries like BigIntbut I was wondering if anybody has any good ideas on how to remove the leading zeros in the corner cases like the one below?
static void Main(string[] args)
{
Console.WriteLine(Subtract("10", "10005"));
}
static string ReverseInput(string inputString)
{
char[] charArray = inputString.ToCharArray();
Array.Reverse(charArray);
return new string(charArray);
}
static string Subtract(string firstNumInput, string secondNumInput)
{
string firstNum = String.Empty;
string secondNum = String.Empty;
bool negative = false;
// Reverse order of string input
if (firstNumInput.Length > secondNumInput.Length)
{
firstNum = ReverseInput(firstNumInput);
secondNum = ReverseInput(secondNumInput);
}
else if (firstNumInput.Length < secondNumInput.Length)
{
negative = true;
firstNum = ReverseInput(secondNumInput);
secondNum = ReverseInput(firstNumInput);
}
else if (firstNumInput.Length == secondNumInput.Length)
{
// iterate through string to find largest
}
char[] result = new char[firstNum.Length + 1];
int resultLength = 0;
int carry = 0;
for (int i = 0; i < firstNum.Length; i++)
{
int an = (i < firstNum.Length) ? int.Parse(firstNum[i].ToString()) : 0;
int bn = (i < secondNum.Length) ? int.Parse(secondNum[i].ToString()) : 0;
int rn = an - bn - carry;
if (rn < 0)
{
carry = 1;
rn += 10;
}
else
{
carry = 0;
}
result[resultLength++] = (char)(rn + '0');
}
// create the result string from the char array
string finalResult = ReverseInput(new string(result, 0, resultLength));
if (negative)
{
finalResult = '-' + finalResult;
}
return finalResult;
}
Are you looking for TrimStart?
// create the result string from the char array
string finalResult = ReverseInput(new string(result, 0, resultLength)).TrimStart('0');
My goal is to be able to convert a string into binary code that is still a string. I am able to turn the string into byte[] but not back to a string without decoding it.
You can use the Convert method for that:
byte [] bytesToEncode = Encoding.UTF8.GetBytes (inputText);
string encodedText = Convert.ToBase64String (bytesToEncode);
If you can encode/decode a byte, e.g.
private static String ToBinary(Byte value) {
StringBuilder Sb = new StringBuilder(8);
Sb.Length = 8;
for (int i = 0; i < 8; ++i) {
Sb[7 - i] = (Char) ('0' + value % 2);
value /= 2;
}
return Sb.ToString();
}
private static Byte FromBinary(String value) {
int result = 0;
for (int i = 0; i < value.Length; ++i)
result = result * 2 + value[i] - '0';
return (Byte) result;
}
You can easily encode/decode a whole string:
// Encoding...
String source = "abc";
// 011000010110001001100011
String result = String.Join("", UTF8Encoding.UTF8.GetBytes(source).Select(x => ToBinary(x)));
...
// Decoding...
List<Byte> codes = new List<Byte>();
for (int i = 0; i < result.Length; i += 8)
codes.Add(FromBinary(result.Substring(i, 8)));
// abc
String sourceBack = UTF8Encoding.UTF8.GetString(codes.ToArray());
use
string str = "Welcome";
byte []arr = System.Text.Encoding.ASCII.GetBytes(str);
I've written the following code in C# for obtaining the length of longest common subsequence of two texts given by use, but it doesn't work with large strings. Could you please help me. I'm really confused.
public Form1()
{
InitializeComponent();
}
public int lcs(char[] s1, char[] s2, int s1size, int s2size)
{
if (s1size == 0 || s2size == 0)
{
return 0;
}
else
{
if (s1[s1size - 1] == s2[s2size - 1])
{
return (lcs(s1, s2, s1size - 1, s2size - 1) + 1);
}
else
{
int x = lcs(s1, s2, s1size, s2size - 1);
int y = lcs(s1, s2, s1size - 1, s2size);
if (x > y)
{
return x;
}
else
return y;
}
}
}
private void button1_Click(object sender, EventArgs e)
{
string st1 = textBox2.Text.Trim(' ');
string st2 = textBox3.Text.Trim(' ');
char[] a = st1.ToCharArray();
char[] b = st2.ToCharArray();
int s1 = a.Length;
int s2 = b.Length;
textBox1.Text = lcs(a, b, s1, s2).ToString();
}
Here you are using the Recursion method. So it leads the program to occur performance problems as you mentioned.
Instead of recursion, use the dynamic programming approach.
Here is the C# Code.
public static void LCS(char[] str1, char[] str2)
{
int[,] l = new int[str1.Length, str2.Length];
int lcs = -1;
string substr = string.Empty;
int end = -1;
for (int i = 0; i < str1.Length; i++)
{
for (int j = 0; j < str2.Length; j++)
{
if (str1[i] == str2[j])
{
if (i == 0 || j == 0)
{
l[i, j] = 1;
}
else
l[i, j] = l[i - 1, j - 1] + 1;
if (l[i, j] > lcs)
{
lcs = l[i, j];
end = i;
}
}
else
l[i, j] = 0;
}
}
for (int i = end - lcs + 1; i <= end; i++)
{
substr += str1[i];
}
Console.WriteLine("Longest Common SubString Length = {0}, Longest Common Substring = {1}", lcs, substr);
}
Here is a solution how to find the longest common substring in C#:
public static string GetLongestCommonSubstring(params string[] strings)
{
var commonSubstrings = new HashSet<string>(strings[0].GetSubstrings());
foreach (string str in strings.Skip(1))
{
commonSubstrings.IntersectWith(str.GetSubstrings());
if (commonSubstrings.Count == 0)
return string.Empty;
}
return commonSubstrings.OrderByDescending(s => s.Length).DefaultIfEmpty(string.Empty).First();
}
private static IEnumerable<string> GetSubstrings(this string str)
{
for (int c = 0; c < str.Length - 1; c++)
{
for (int cc = 1; c + cc <= str.Length; cc++)
{
yield return str.Substring(c, cc);
}
}
}
I found it here: http://www.snippetsource.net/Snippet/75/longest-common-substring
Just for fun, here is one example using Queue<T>:
string LongestCommonSubstring(params string[] strings)
{
return LongestCommonSubstring(strings[0], new Queue<string>(strings.Skip(1)));
}
string LongestCommonSubstring(string x, Queue<string> strings)
{
if (!strings.TryDequeue(out var y))
{
return x;
}
var output = string.Empty;
for (int i = 0; i < x.Length; i++)
{
for (int j = x.Length - i; j > -1; j--)
{
string common = x.Substring(i, j);
if (y.IndexOf(common) > -1 && common.Length > output.Length) output = common;
}
}
return LongestCommonSubstring(output, strings);
}
It's still using recursion though, but it's a nice example of Queue<T>.
I refactored the C++ code from Ashutosh Singh at https://iq.opengenus.org/longest-common-substring-using-rolling-hash/ to create a rolling hash approach in C# - this will find the substring in O(N * log(N)^2) time and O(N) space
using System;
using System.Collections.Generic;
public class RollingHash
{
private class RollingHashPowers
{
// _mod = prime modulus of polynomial hashing
// any prime number over a billion should suffice
internal const int _mod = (int)1e9 + 123;
// _hashBase = base (point of hashing)
// this should be a prime number larger than the number of characters used
// in my use case I am only interested in ASCII (256) characters
// for strings in languages using non-latin characters, this should be much larger
internal const long _hashBase = 257;
// _pow1 = powers of base modulo mod
internal readonly List<int> _pow1 = new List<int> { 1 };
// _pow2 = powers of base modulo 2^64
internal readonly List<long> _pow2 = new List<long> { 1L };
internal void EnsureLength(int length)
{
if (_pow1.Capacity < length)
{
_pow1.Capacity = _pow2.Capacity = length;
}
for (int currentIndx = _pow1.Count - 1; currentIndx < length; ++currentIndx)
{
_pow1.Add((int)(_pow1[currentIndx] * _hashBase % _mod));
_pow2.Add(_pow2[currentIndx] * _hashBase);
}
}
}
private class RollingHashedString
{
readonly RollingHashPowers _pows;
readonly int[] _pref1; // Hash on prefix modulo mod
readonly long[] _pref2; // Hash on prefix modulo 2^64
// Constructor from string:
internal RollingHashedString(RollingHashPowers pows, string s, bool caseInsensitive = false)
{
_pows = pows;
_pref1 = new int[s.Length + 1];
_pref2 = new long[s.Length + 1];
const long capAVal = 'A';
const long capZVal = 'Z';
const long aADif = 'a' - 'A';
unsafe
{
fixed (char* c = s)
{
// Fill arrays with polynomial hashes on prefix
for (int i = 0; i < s.Length; ++i)
{
long v = c[i];
if (caseInsensitive && capAVal <= v && v <= capZVal)
{
v += aADif;
}
_pref1[i + 1] = (int)((_pref1[i] + v * _pows._pow1[i]) % RollingHashPowers._mod);
_pref2[i + 1] = _pref2[i] + v * _pows._pow2[i];
}
}
}
}
// Rollingnomial hash of subsequence [pos, pos+len)
// If mxPow != 0, value automatically multiply on base in needed power.
// Finally base ^ mxPow
internal Tuple<int, long> Apply(int pos, int len, int mxPow = 0)
{
int hash1 = _pref1[pos + len] - _pref1[pos];
long hash2 = _pref2[pos + len] - _pref2[pos];
if (hash1 < 0)
{
hash1 += RollingHashPowers._mod;
}
if (mxPow != 0)
{
hash1 = (int)((long)hash1 * _pows._pow1[mxPow - (pos + len - 1)] % RollingHashPowers._mod);
hash2 *= _pows._pow2[mxPow - (pos + len - 1)];
}
return Tuple.Create(hash1, hash2);
}
}
private readonly RollingHashPowers _rhp;
public RollingHash(int longestLength = 0)
{
_rhp = new RollingHashPowers();
if (longestLength > 0)
{
_rhp.EnsureLength(longestLength);
}
}
public string FindCommonSubstring(string a, string b, bool caseInsensitive = false)
{
// Calculate max neede power of base:
int mxPow = Math.Max(a.Length, b.Length);
_rhp.EnsureLength(mxPow);
// Create hashing objects from strings:
RollingHashedString hash_a = new RollingHashedString(_rhp, a, caseInsensitive);
RollingHashedString hash_b = new RollingHashedString(_rhp, b, caseInsensitive);
// Binary search by length of same subsequence:
int pos = -1;
int low = 0;
int minLen = Math.Min(a.Length, b.Length);
int high = minLen + 1;
var tupleCompare = Comparer<Tuple<int, long>>.Default;
while (high - low > 1)
{
int mid = (low + high) / 2;
List<Tuple<int, long>> hashes = new List<Tuple<int, long>>(a.Length - mid + 1);
for (int i = 0; i + mid <= a.Length; ++i)
{
hashes.Add(hash_a.Apply(i, mid, mxPow));
}
hashes.Sort(tupleCompare);
int p = -1;
for (int i = 0; i + mid <= b.Length; ++i)
{
if (hashes.BinarySearch(hash_b.Apply(i, mid, mxPow), tupleCompare) >= 0)
{
p = i;
break;
}
}
if (p >= 0)
{
low = mid;
pos = p;
}
else
{
high = mid;
}
}
// Output answer:
return pos >= 0
? b.Substring(pos, low)
: string.Empty;
}
}