Replace consecutive numbers with mathematical form [closed] - algorithm

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Improve this question
I am working on compression algorithm for which I want to replace all consecutive number with its mathematical form which is not logical mathematically but my algorithm will know and convert it to original form.
Suppose I have string:
string input = "732183900000000000002389288888888888888";
Did you see it have 0000000000 and 8888888888888 are major consecutive duplicates.
And now I want to convert those to:
//convert 000000000 to 0*9. Means 9 times 0.
//convert 888888888 to 8*9. Means 8 times 0.
string output = "7321839" +
"0*13" +
"23892" +
"8*14";
//or
string output = "7321839-0*13-23892-8*14";
Points to consider:
Any language that works on windows will be accepted. For me main thing is algorithm.
Please keep performance in mind as it would be used for big files.

To be honest this is as simple as it gets:
Parse through the string one character at a time.
Check if the previous character is the same as the current one.
If it is same then increment a counter variable or else reset it to 0.
If the counter value is greater than one when we reset the counter to 0 then add * to the result.

Regex might be a bit convoluted for this given the rules for dashes (although not impossible by any means),
Seemingly, you want the following
Groups of the same number greater than the count of 1
No prefix dash
No suffix dash
No double dashes (speculative)
Here is a fairly efficient C# O(n) implementation with StringBuilder, which inurn should allow you to work with exceedingly large strings with minimal allocations
Given
public static string Shorten(string value)
{
var sb = new StringBuilder(value.Length);
int i, last;
var isLastGroup = false;
void Write()
{
var isGroup = i - last > 1;
var getDash = last == 0 || isLastGroup ? "" : "-";
sb.Append(isGroup ? $"{getDash}{value[last]}*{i - last}{(i != value.Length ? "-" : "")}" : value[last].ToString());
isLastGroup = isGroup;
last = i;
}
for (i = 0, last = 0; i < value.Length; i++)
if (value[last] != value[i])
Write();
Write();
return sb.ToString();
}
Tests
Console.WriteLine(Shorten("1"));
Console.WriteLine(Shorten("111"));
Console.WriteLine(Shorten("1112"));
Console.WriteLine(Shorten("1222"));
Console.WriteLine(Shorten("12233344445555512345"));
Results
1
13
13-2
1-23
1-22-33-44-5*5-12345
Full Demo Here

Related

Fuzzy string record search algorithm (supporting word transpose and character transpose)

I am trying to find the best algorithm for my particular application. I have searched around on SO, Google, read various articles about Levenshtein distances, etc. but honestly it's a bit out of my area of expertise. And most seem to find how similar two input strings are, like a Hamming distance between strings.
What I'm looking for is different, more of a fuzzy record search (and I'm sure there is a name for it, that I don't know to Google). I am sure someone has solved this problem before and I'm looking for a recommendation to point me in the right direction for my further research.
In my case I am needing a fuzzy search of a database of entries of music artists and their albums. As you can imagine, the database will have millions of entries so an algorithm that scales well is crucial. It's not important to my question that Artist and Album are in different columns, the database could just store all words in one column if that helped the search.
The database to search:
|-------------------|---------------------|
| Artist | Album |
|-------------------|---------------------|
| Alanis Morissette | Jagged Little Pill |
| Moby | Everything is Wrong |
| Air | Moon Safari |
| Pearl Jam | Ten |
| Nirvana | Nevermind |
| Radiohead | OK Computer |
| Beck | Odelay |
|-------------------|---------------------|
The query text will contain from just one word in the entire Artist_Album concatenation up to the entire thing. The query text is coming from OCR and is likely to have single character transpositions but the most likely thing is the words are not guaranteed to have the right order. Additionally, there could be extra words in the search that aren't a part of the album (like cover art text). For example, "OK Computer" might be at the top of the album and "Radiohead" below it, or some albums have text arranged in columns which intermixes the word orders.
Possible search strings:
C0mputer Rad1ohead
Pearl Ten Jan
Alanis Jagged Morisse11e Litt1e Pi11
Air Moon Virgin Records
Moby Everything
Note that with OCR, some letters will look like numbers, or the wrong letter completely (Jan instead of Jam). And in the case of Radiohead's OK Computer and Moby's Everything Is Wrong, the query text doesn't even have all of the words. In the case of Air's Moon Safari, the extra words Virgin Records are searched, but Safari is missing.
Is there a general algorithm that could return the single likeliest result from the database, and if none meet some "likeliness" score threshold, it returns nothing? I'm actually developing this in Python, but that's just a bonus, I'm looking more for where to get started researching.
Let's break the problem down in two parts.
First, you want to define some measure of likeness (this is called a metric). This metric should return a small number if the query text closely matches the album/artist cover, and return a larger number otherwise.
Second, you want a datastructure that speeds up this process. Obviously, you don't want to calculate this metric every single time a query is ran.
part 1: the metric
You already mentioned Levenshtein distance, which is a great place to start.
Think outside the box though.
LD makes certain assumptions (each character replacement is equally likely, deletion is equally likely as insertion, etc). You can obviously improve the performance of this metric by taking into account what faults OCR is likely to introduce.
E.g. turning a '1' into an 'i' should not be penalized as harshly as turning a '0' into an '_'.
I would implement the metric in two stages. For any given two strings:
split both strings in tokens (assume space as the separator)
look for the most similar words (using a modified version of LD)
assign a final score based on 'matching words', 'missing words' and 'added words' (preferably weighted)
This is an example implementation (fiddle around with the constants):
static double m(String a, String b){
String[] aParts = a.split(" ");
String[] bParts = b.split(" ");
boolean[] bUsed = new boolean[bParts.length];
int matchedTokens = 0;
int tokensInANotInB = 0;
int tokensInBNotInA = 0;
for(int i=0;i<aParts.length;i++){
String a0 = aParts[i];
boolean wasMatched = true;
for(int j=0;j<bParts.length;j++){
String b0 = bParts[j];
double d = levenshtein(a0, b0);
/* If we match the token a0 with a token from b0
* update the number of matchedTokens
* escape the loop
*/
if(d < 2){
bUsed[j]=true;
wasMatched = true;
matchedTokens++;
break;
}
}
if(!wasMatched){
tokensInANotInB++;
}
}
for(boolean partUsed : bUsed){
if(!partUsed){
tokensInBNotInA++;
}
}
return (matchedTokens
+ tokensInANotInB * -0.3 // the query is allowed to contain extra words at minimal cost
+ tokensInBNotInA * -0.5 // the album title should not contain too many extra words
) / java.lang.Math.max(aParts.length, bParts.length);
}
This function uses a modified levenshtein function:
static double levenshtein(String x, String y) {
double[][] dp = new double[x.length() + 1][y.length() + 1];
for (int i = 0; i <= x.length(); i++) {
for (int j = 0; j <= y.length(); j++) {
if (i == 0) {
dp[i][j] = j;
}
else if (j == 0) {
dp[i][j] = i;
}
else {
dp[i][j] = min(dp[i - 1][j - 1]
+ costOfSubstitution(x.charAt(i - 1), y.charAt(j - 1)),
dp[i - 1][j] + 1,
dp[i][j - 1] + 1);
}
}
}
return dp[x.length()][y.length()];
}
Which uses the function 'cost of substitution' (which works as explained)
static double costOfSubstitution(char a, char b){
if(a == b)
return 0.0;
else{
// 1 and i
if(a == '1' && b == 'i')
return 0.5;
if(a == 'i' && b == '1')
return 0.5;
// 0 and O
if(a == '0' && b == 'o')
return 0.5;
if(a == 'o' && b == '0')
return 0.5;
if(a == '0' && b == 'O')
return 0.5;
if(a == 'O' && b == '0')
return 0.5;
// default
return 1.0;
}
}
I only included a couple of examples (turning '1' into 'i' or '0' into 'o').
But I'm sure you get the idea.
part 2: the datastructure
Look into BK-trees. They are a specific datastructure to hold metric information. Your metric needs to be a genuine metric (in the mathematical sense of the word). But that's easily arranged.

UVa Live 6823 Algorithm [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Improve this question
How does the following code work for the problem Uva Live 6823?
I just want an explanation for the algorithm used in this code.
Thanks in advance.
#include <bits/stdc++.h>
using namespace std;
int main()
{
string st;
while(cin>> st){
long long int cnt=0,x=0,arr[]={1,0,0};
for(int a=0;a<st.length();a++){
if(!isdigit(st[a])){
x=0;
arr[0]=1;
arr[1]=arr[2]=0;
}
else{
x=(x+st[a]-48)%3;
cnt+=arr[x];
arr[x]++;
}
}
cout<< cnt <<endl;
}
return 0;
}
This algorithm makes use of the divisibility by 3 rule, namely, if the sum of the digits of a number are divisible by 3, then the number is divisible by 3. It keeps track of the frequency of occurrence for the remainder of the sum of the digits, divided by 3 (eg. 0, 1 or 2). Anytime a new digit is added, the substrings with the same remainder adds another permutation substring that is divisible by 3. Note: the constant used 48 refers to the ASCII value for '0'.
This is probably best illustrated by example. Consider the string 12321, being processed digit by digit.
// "iteration" #0 (st[a] = '')
x = 0
arr[] = {1,0,0}
cnt = 0
// iteration #1 (st[a]='1'). '1' is not divisible by three.
x = 1
arr[] = {1,1,0}
cnt = 0
// iteration #2 (st[a]='2'). '12' is divisible by three.
x = 0
arr[] = {2,1,0}
cnt = 1
// iteration #3 (st[a]='3'). '123' and '3' are divisible by three
x = 0
arr[] = {3,1,0}
cnt = 3
// iteration #4 (st[a]='2'). No new divisible by three substrings
x = 2
arr[] = {3,1,1}
cnt = 3
// iteration #5 (st[a]='1'). '12321', '321' and '21' are divisible by 3.
// note, the other times when x = 0, st[0..a] = '', '12', '123'
x = 0
arr[] = {4,1,1}
cnt = 6
You could continue adding digits, and see how it works. When it hits a non-digit character, the counting resets, because any non-digit character cannot participate in a permutation.

Algorithm(s) to use to generate melodies? [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 years ago.
Improve this question
I am working at a small program to generate random melodies. So far I have an array of notes with their frequencies, the program picks a random element from the array and plays its frequency. The problem is that the melody "sounds bad": as if you sat in front of the piano and played random keys. I need an algorithm to generate a melody, with frequencies and timings and so on. I don't need anything elaborate, just make a melody that sounds good.
Use existing music (MIDI?) to train a Markov model or Markov chain
. You may want to transpose the input to the same key(s) e.g. C major, A minor. The pitch*duration will probably have to be encoded (enumerated) into a single dimension (x*7*5, or x*12*5) . [x := number of octaves in your input range, 5=duration, 1 downto 1/16th ]
Extra bonus for loops and variations, but that will be much harder...
If you want the music to be "airy" and never dissonant then use Pentatonic Scales (i.e Like playing the black keys of the piano)
For example:
Pitch Selection:
C#,D#,F#,G# or A#
Pitch Range:
3 to 5 Octaves (since an Octave up or down is basically doubling or halving the frequency of your base frequency)
Note Duration:
Range from 16th to Whole Note
Others have tried this. You might like to read the article 'Music and Fractal Landscapes' by Richard MacDuff.
Try playing notes from the same scale :) C major, D minor or so ;)
Or even the pentatonic.
import java.io.;import javax.sound.sampled.;class Me{public static void main(String[] args) { int fij=128; int tw=64; int fd=2048; int secs=128; String fo="Me.wav"; int ho=8; if(args.length==5){ fij=4*Integer.parseInt(args[0]); tw=4*Integer.parseInt(args[1]); fd=4*Integer.parseInt(args[2]); ho=4+Integer.parseInt(args[3]); fo=args[4]; } double[] pcm_datadrd = new double[44100*secs]; double[] pcm_datadr = new double[44100]; for (int i=0; i<pcm_datadr.length; i++) { pcm_datadr[i]=( 64*Math.sin((i/1837.5*(i*i/128))*Math.PI*2)/(1+(Math.cos((4096) +64))+(i/256)) ); } for(int x=0;x<(secs-1);x++){ for (int i=0; i<pcm_datadr.length; i++) { pcm_datadrd[((int)(44100*x)+i)] = (double) ( 128*Math.sin((i/1837.5*(i*i/256))*Math.PI*2)/(1+(Math.cos((4096) +64))+((double)i/256)) ); } } byte[] pcm_data = new byte[44100*secs]; for (int i=0; i<pcm_data.length; i++) { double fup=1+(fij*i/pcm_data.length); pcm_data[i]=(byte)(pcm_datadrd[i]+(64*Math.sin((i/twfupMath.cos(3+(i/fd))))+ 32*Math.sin(5+(i/ho*fd)*Math.cos(i/(fd+tw))))); } AudioFormat frmt = new AudioFormat(44100, 8, 1, true, true); AudioInputStream ais = new AudioInputStream( new ByteArrayInputStream(pcm_data), frmt, pcm_data.length / frmt.getFrameSize() ); try { AudioSystem.write(ais, AudioFileFormat.Type.WAVE, new File(fo)); }catch(Exception e) { e.printStackTrace(); } }}
ABBA 'k vind't voor de kleintjes zielig die me laten MMA je, je
vergeetk wat en huilt ook weet ik tranen
It's better not randomly create a block A of random notes and B random notes and Do (ABBA)+ or (AB)AB* but better apply the concept of Quantum Theory (everything in nature interacts, like our house-rules here see are applied in ervery house in the world), as you now understand.
So like, a cellular automata, 'game of life' you can, per step, play your 'true' notes, letting the 'y-axis' the note number and (please count) the duration of a note (adjacing trues along the 'x-axis')
Example of this sound for randomness sake:
http://simple.ogena.net/Steel_Drum-Slap_Bas.mp3
from generated source:
http://simple.ogena.net/Steel_Drum-Slap_Bas.mid

What is the best algorithm to find whether an anagram is of a palindrome?

In this problem we consider only strings of lower-case English letters (a-z).
A string is a palindrome if it has exactly the same sequence of characters when traversed left-to-right as right-to-left. For example, the following strings are palindromes:
"kayak"
"codilitytilidoc"
"neveroddoreven"
A string A is an anagram of a string B if it consists of exactly the same characters, but possibly in another order. For example, the following strings are each other's anagrams:
A="mary" B="army" A="rocketboys" B="octobersky" A="codility" B="codility"
Write a function
int isAnagramOfPalindrome(String S);
which returns 1 if the string s is a anagram of some palindrome, or returns 0 otherwise.
For example your function should return 1 for the argument "dooernedeevrvn", because it is an anagram of a palindrome "neveroddoreven". For argument "aabcba", your function should return 0.
'Algorithm' would be too big word for it.
You can construct a palindrome from the given character set if each character occurs in that set even number of times (with possible exception of one character).
For any other set, you can easily show that no palindrome exists.
Proof is simple in both cases, but let me know if that wasn't clear.
In a palindrome, every character must have a copy of itself, a "twin", on the other side of the string, except in the case of the middle letter, which can act as its own twin.
The algorithm you seek would create a length-26 array, one for each lowercase letter, and start counting the characters in the string, placing the quantity of character n at index n of the array. Then, it would pass through the array and count the number of characters with an odd quantity (because one letter there does not have a twin). If this number is 0 or 1, place that single odd letter in the center, and a palindrome is easily generated. Else, it's impossible to generate one, because two or more letters with no twins exist, and they can't both be in the center.
I came up with this solution for Javascript.
This solution is based on the premise that a string is an anagram of a palindrome if and only if at most one character appears an odd number of times in it.
function solution(S) {
var retval = 0;
var sorted = S.split('').sort(); // sort the input characters and store in
// a char array
var array = new Array();
for (var i = 0; i < sorted.length; i++) {
// check if the 2 chars are the same, if so copy the 2 chars to the new
// array
// and additionally increment the counter to account for the second char
// position in the loop.
if ((sorted[i] === sorted[i + 1]) && (sorted[i + 1] != undefined)) {
array.push.apply(array, sorted.slice(i, i + 2));
i = i + 1;
}
}
// if the original string array's length is 1 or more than the length of the
// new array's length
if (sorted.length <= array.length + 1) {
retval = 1;
}
//console.log("new array-> " + array);
//console.log("sorted array-> " + sorted);
return retval;
}
i wrote this code in java. i don't think if its gonna be a good one ^^,
public static int isAnagramOfPalindrome(String str){
ArrayList<Character> a = new ArrayList<Character>();
for(int i = 0; i < str.length(); i++){
if(a.contains(str.charAt(i))){
a.remove((Object)str.charAt(i));
}
else{
a.add(str.charAt(i));
}
}
if(a.size() > 1)
return 0;
return 1;
}
Algorithm:
Count the number of occurrence of each character.
Only one character with odd occurrence is allowed since in a palindrome the maximum number of character with odd occurrence can be '1'.
All other characters should occur in an even number of times.
If (2) and (3) fail, then the given string is not a palindrome.
This adds to the other answers given. We want to keep track of the count of each letter seen. If we have more than one odd count for a letter then we will not be able to form a palindrome. The odd count would go in the middle, but only one odd count can do so.
We can use a hashmap to keep track of the counts. The lookup for a hashmap is O(1) so it is fast. We are able to run the whole algorithm in O(n). Here's it is in code:
if __name__ == '__main__':
line = input()
dic = {}
for i in range(len(line)):
ch = line[i]
if ch in dic:
dic[ch] += 1
else:
dic[ch] = 1
chars_whose_count_is_odd = 0
for key, value in dic.items():
if value % 2 == 1:
chars_whose_count_is_odd += 1
if chars_whose_count_is_odd > 1:
print ("NO")
else:
print ("YES")
I have a neat solution in PHP posted in this question about complexities.
class Solution {
// Function to determine if the input string can make a palindrome by rearranging it
static public function isAnagramOfPalindrome($S) {
// here I am counting how many characters have odd number of occurrences
$odds = count(array_filter(count_chars($S, 1), function($var) {
return($var & 1);
}));
// If the string length is odd, then a palindrome would have 1 character with odd number occurrences
// If the string length is even, all characters should have even number of occurrences
return (int)($odds == (strlen($S) & 1));
}
}
echo Solution :: isAnagramOfPalindrome($_POST['input']);
It uses built-in PHP functions (why not), but you can make it yourself, as those functions are quite simple. First, the count_chars function generates a named array (dictionary in python) with all characters that appear in the string, and their number of occurrences. It can be substituted with a custom function like this:
$count_chars = array();
foreach($S as $char) {
if array_key_exists($char, $count_chars) {
$count_chars[$char]++;
else {
$count_chars[$char] = 1;
}
}
Then, an array_filter with a count function is applied to count how many chars have odd number of occurrences:
$odds = 0;
foreach($count_chars as $char) {
$odds += $char % 2;
}
And then you just apply the comparison in return (explained in the comments of the original function).
return ($odds == strlen($char) % 2)
This runs in O(n). For all chars but one, must be even. the optional odd character can be any odd number.
e.g.
abababa
def anagram_of_pali(str):
char_list = list(str)
map = {}
nb_of_odds = 0
for char in char_list:
if char in map:
map[char] += 1
else:
map[char] = 1
for char in map:
if map[char] % 2 != 0:
nb_of_odds += 1
return True if nb_of_odds <= 1 else False
You just have to count all the letters and check if there are letters with odd counts. If there are more than one letter with odd counts the string does not satisfy the above palindrome condition.
Furthermore, since a string with an even number letters must not have a letter with an odd count it is not necessary to check whether string length is even or not. It will take O(n) time complexity:
Here's the implementation in javascript:
function canRearrangeToPalindrome(str)
{
var letterCounts = {};
var letter;
var palindromeSum = 0;
for (var i = 0; i < str.length; i++) {
letter = str[i];
letterCounts[letter] = letterCounts[letter] || 0;
letterCounts[letter]++;
}
for (var letterCount in letterCounts) {
palindromeSum += letterCounts[letterCount] % 2;
}
return palindromeSum < 2;
}
All right - it's been a while, but as I was asked such a question in a job interview I needed to give it a try in a few lines of Python. The basic idea is that if there is an anagram that is a palindrome for even number of letters each character occurs twice (or something like 2n times, i.e. count%2==0). In addition, for an odd number of characters one character (the one in the middle) may occur only once (or an uneven number - count%2==1).
I used a set in python to get the unique characters and then simply count and break the loop once the condition cannot be fulfilled. Example code (Python3):
def is_palindrome(s):
letters = set(s)
oddc=0
fail=False
for c in letters:
if s.count(c)%2==1:
oddc = oddc+1
if oddc>0 and len(s)%2==0:
fail=True
break
elif oddc>1:
fail=True
break
return(not fail)
def is_anagram_of_palindrome(S):
L = [ 0 for _ in range(26) ]
a = ord('a')
length = 0
for s in S:
length += 1
i = ord(s) - a
L[i] = abs(L[i] - 1)
return length > 0 and sum(L) < 2 and 1 or 0
While you can detect that the given string "S" is a candidate palindrome using the given techniques, it is still not very useful. According to the implementations given,
isAnagramOfPalindrome("rrss") would return true but there is no actual palindrome because:
A palindrome is a word, phrase, number, or other sequence of symbols or elements, whose meaning may be interpreted the same way in either forward or reverse direction. (Wikipedia)
And Rssr or Srrs is not an actual word or phrase that is interpretable. Same with it's anagram. Aarrdd is not an anagram of radar because it is not interpretable.
So, the solutions given must be augmented with a heuristic check against the input to see if it's even a word, and then a verification (via the implementations given), that it is palindrome-able at all. Then there is a heuristic search through the collected buckets with n/2! permutations to search if those are ACTUALLY palindromes and not garbage. The search is only n/2! and not n! because you calculate all permutations of each repeated letter, and then you mirror those over (in addition to possibly adding the singular pivot letter) to create all possible palindromes.
I disagree that algorithm is too big of a word, because this search can be done pure recursively, or using dynamic programming (in the case of words with letters with occurrences greater than 2) and is non trivial.
Here's some code: This is same as the top answer that describes algorithm.
1 #include<iostream>
2 #include<string>
3 #include<vector>
4 #include<stack>
5
6 using namespace std;
7
8 bool fun(string in)
9 {
10 int len=in.size();
11 int myints[len ];
12
13 for(int i=0; i<len; i++)
14 {
15 myints[i]= in.at(i);
16 }
17 vector<char> input(myints, myints+len);
18 sort(input.begin(), input.end());
19
20 stack<int> ret;
21
22 for(int i=0; i<len; i++)
23 {
24 if(!ret.empty() && ret.top()==input.at(i))
25 {
26 ret.pop();
27 }
28 else{
29 ret.push(input.at(i));
30 }
31 }
32
33 return ret.size()<=1;
34
35 }
36
37 int main()
38 {
39 string input;
40 cout<<"Enter word/number"<<endl;
41 cin>>input;
42 cout<<fun(input)<<endl;
43
44 return 0;
45 }

Derangement or what? [closed]

Closed. This question is off-topic. It is not currently accepting answers.
Want to improve this question? Update the question so it's on-topic for Stack Overflow.
Closed 12 years ago.
Improve this question
I was looking at the description of a derangement that states "a derangement is a permutation of the elements of a set such that none of the elements appear in their original position". But then it gave 9 derangements for a set of 4 items. That doesn't make sense to me, because I only get 4 discrete sets from 4 items.
For example:
1234
3142
2413
4321
Is there a different term than derangement for sets where the numbers don't have the same order as in any other set, based on a particular number of items?
And does anyone know of an algorithm for generating the derangements?
The nine derangements are:
2143
2341
2413
3142
3412
3421
4123
4312
4321
As you can see, column 1 does not contain 1, column 2 does not contain 2 and so on. In addition, every row has the numbers 1, 2, 3 and 4 and there are no duplicates (they're sorted to make that easier to detect).
For what it's worth, that was discovered with a brute force attack (the attack space was relatively small):
#include <stdio.h>
int main (void) {
int a, b, c, d, skip;
for (a = 1; a <= 4; a++) {
for (b = 1; b <= 4; b++) {
for (c = 1; c <= 4; c++) {
for (d = 1; d <= 4; d++) {
skip = (a==1) || (b==2) || (c==3) || (d==4);
skip |= (a==b) || (a==c) || (a==d);
skip |= (b==c) || (b==d);
skip |= (c==d);
if (!skip) {
printf ("%d%d%d%d\n", a, b, c, d);
}
}
}
}
}
return 0;
}
It turned out that I was looking for a complete latin square (row-complete and or column-complete). I had already coded an algorithm to detect those, but didn't know if this kind of thing had a name. That's it, thanks.

Resources